装配Bean:即依赖注入
在Spring中,对象间的协作是通过IoC机制完成的。反向控制也叫依赖注入(Dependency Injection,DI),简单来说就是将JavaBean需要的对象通过配置文件加载进来。
Spring提供了两种装配Bean的容器,一是BeanFactoy,另一个是ApplicationContext。两者做为容器,所有的Bean都应该通过容器装配,而容器也知道自己都装配了哪些Bean。
Bean装配实际上就是让容器知道程序中都有哪些Bean,可以通过以下两种方式实现:配置文件(最为常用,体现了依赖注入DI的思想)编程方式(写的过程中向BeanFactory去注册)
ApplicationContext与BeanFactory都是接口,ApplicationContext是由BeanFactory接口扩展而来,它增强了BeanFactory的功能。
Bean容器能够加载的对象并不一定是严格遵循JavaBeans规范的Java类,任何可实例化的类都可以通过Spring Bean容器加载进来。通常称这些类为POJO。
要记住,BeanFactory不仅仅只具备实例化Bean的功能,它还知道所有的Bean,可以配置和管理它们。
Spring给出一些BeanFactory的实现类,其中最为常用的是XmlBeanFactory。1、通过文件系统Resource res = new FileSystemResource("beans.xml");XmlBeanFactory factory = new XmlBeanFactory(res);2、通过类路径ClassPathResource res = new ClassPathResource("beans.xml");XmlBeanFactory factory = new XmlBeanFactory(res);3、通过ApplicationContext加载ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml", "applicationContext-part2.xml"});BeanFactory factory = (BeanFactory) appContext;
sping-frameword-1.2.7-with-dependencies中的dist里面是spring的包将其中的spring.jar加入工程的类路径里面,下一步是写个javaBean
package bean ;
public class HelloBean{ public HelloBean { System.out.println("init!"); } private String msg ;
//加上getter和setter方法};
public class Demo{ public static void main(String[] args) { Resource res = new ClassPathResource("bean.xml") ; //配置文件放在类路径的根路径下 BeanFactory factory = new XmlBeanFactory(res) ; //在dist文件夹里面有spring-beans的dtd文档 HelloBean bean = (HelloBean)factory.getBean("hello") ; //根据xml配置文件中的id号去找bean,然后从这个工厂中取出bean System.out.println(bean.getMsg());
HelloBean bean1 = (HelloBean)factory.getBean("hello") ; System.out.println(bean==bean1); //因为配置文件中的singleton设置为true, //故是单例模式,所以两个bean肯定一样,所以打印true
//只有ApplicationContext才能实现bean的非延迟加载功能,所以要想让bean中的 //"init!"字符串在"before getbean!"字符串之前显示的话,那么首先必须得是单例模式, //然后还得设置lazy-init="false",才可以做到,如果singleton="false"的话,是无法 //做到非延迟加载的!可以通过下面的代码来验证,看"before getbean!"和"init!"这两个 //字符串出现的先后顺序就一目了然了。 ApplicationContext factory = new ClassPathXmlApplicationContext("bean.xml"); System.out.println("before getbean!"); HelloBean bean2 = (HelloBean)factory.getBean("hello"); }}
配置文件bean.xml:
将<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">拷贝进去……
<beans>//singleton属性表明容器的bean是否使用单例模式……//默认情况下,beanFactory就是延迟加载的 <bean id="hello" class="bean.HelloBean" singleton="true" lazy-init="false"> <property name="msg"> <value>Hello,Spring!</value> </property> </bean>
</beans>
//上面的配置文件将属性的值注入到bean的属性中,这就是依赖注入的一个例子//同时将那个javaBean注册到容器中通常可以通过三种方式装配Bean
通过setter方法通过构造函数自动装配其中,使用setter方法装配Bean是最为常用的。
如下代码的执行结果是什么?FirstBean bean = (FirstBean)factory.getBean("my");FirstBean bean1 = (FirstBean)factory.getBean("my");System.out.println(bean==bean1);结果依赖于bean元素的singleton属性,如果设置true,则以单例模式加载bean,则永远指向同一个bean;反之则每次实例化一个。也即在单例模式下,BeanFactory维护所有的bean
如果想让一个单例模式的bean被清理掉的话,不能简单的将bean置为null,而是必须使用destroySingletons方法还有一个destroyBean(String beanName,Object bean)这两个方法都可以来垃圾回收单例模式中的bean---------------------------------------------------------------延迟加载:
如下代码执行的结果是什么?System.out.println("before loading");FirstBean bean = (FirstBean)factory.getBean("my");System.out.println(“after loading”);在bean的构造函数中,包含:System.out.println(“bean init”);结果依赖于bean的lazy-init属性,如果设置为true,则在第一次加载该bean时初始化;否则在初始化容器时就加载所有bean。延迟加载仅在bean单例模式下起作用。
-------------------------------------------------------------------------初始化和清理:
Spring的初始化肯定是在依赖注入(属性设置)全部完成后才去做的。框架提供的初始化方法肯定是先于我们自己定义的初始化方法的!比如,如果继承了InitializingBean的话,会有一个这个接口提供的初始化方法,叫做afterPropertySet,它的执行就是先于你定义的初始化方法的……
同理,DisposableBean接口中也定义了一个清理的方法,这个方法肯定也是先于你定义的清理方法的!
建议使用你自己定义的初始化和清理方法,不要使用容器提供的,这样可以避免bean被污染!!!
在许多情况下,需要在对象加载后立即初始化资源,而在删除对象前先清理资源在spring中,提供了两种方式实现初始化和清理工作。通过设置bean的init-method和destroy-method指定初始与清理方法通过实现InitializingBean及DisposableBean接口,由容器在加载与删除bean时自动调用。
//既然定义了一个init方法,所以可以在javaBean中设置下面的初始化方法:public void init(){
}
//同样销毁方法也可以类似的定义:public void destroy(){ }//特别注意,如果你想运行上面自定义的destroy方法的话,你必须首先执行factory.destroySingletons();//否则不会去执行你上面自定义的destroy方法的!
原型bean在创建之后就脱离了factory的管理,所以呢,对于原型bean,只能初始化,而不可以清理
<bean id="first" class="mybeans.FirstBean" init-method="init" destroy-method="destroy" > <property name="message"> <value>Hi, Rod Johnson</value> </property></bean>可以通过destroySingletons方法删除所有单例bean原型bean在创建后即脱离BeanFactory的维护,所以只能调用初始化方法,而不能做清理工作。
===========================================================================
下午课程开始:
public class HelloBean implements BeanNameAware, BeanFactoryAware{ private BeanFactory factory ; private String name ;
public void setBeanName(String a)//由BeanNameAware继承来 { this.name = a ; }
//由BeanFactoryAware继承而来。 public void setBeanFactory(BeanFactory factory) throws BeanException { this.factory = factory }}
-------------------------------------------------------下面说说编程式加载Bean方式:
XmlBeanFactory提供两种编程方式加载Bean:registerSingleton和registerBeanDefinition,前者不参与bean生命周期后者参与bean的生命周期。
public static void main(String[] args){ Resource res = new ClassPathResource("bean.xml");
XmlBeanFactory factory = new XmlBeanFactory(res); factory.registerSingleton("bb",new HelloBean()); HelloBean bean = (HelloBean)factory.getBean("bb"); System.out.println(bean.getMsg());
factory.destroySingletons();}
如果使用BeanDefinition的话
public static void main(String[] args){ Resource res = new ClassPathResource("bean.xml");
XmlBeanFactory factory = new XmlBeanFactory(res); RootBeanDefinition rbd = new RootBeanDefinition(); rbd.setBeanClass(HelloBean.class); rbd.setInitMethodName("init"); //上面这句话告诉Spring自定义的方法名字叫做init,如果不写的话,自定义的 //初始化方法不会执行的。
/* RootBeanDefinition rbd1 = new RootBeanDefinition(Integer.class); ContructorArgumentValues cav = new ConstructorArgumentValues(); cav.addGenericArgumentValue("12"); rbd1.setConstructorArgumentValues(cav); factory.registerBeanDefinition("aa",rbd1); System.out.println(factory.getBean("aa")); */ factory.registerBeanDefinition("aa",rbd);
HelloBean bean = (HelloBean)factory.getBean("aa"); System.out.println(bean.getMsg());
factory.destroySingletons();}
老师的关键代码如下:
XmlBeanFactory factory = new XmlBeanFactory(res);RootBeanDefinition rbd = new RootBeanDefinition();rbd.setBeanClass(HelloBean.class);rbd.setInitMethodName("init");
RootBeanDefinition rbdi = new RootBeanDefinition(Integer.class);ConstructorArgumentValues cav = new ConstructorArgumentValues();cav.addGenericArgumentValue("12");rbdi.setConstructorArgumentValues(cav);
=====================================================================
bean里面的属性是各式各样的,我们上面使用的实体类只是使用了String类型,根本没有考虑其他的类型
bean装配方式:
通过Setter方法、通过构造函数、自动装配三种方法都可以达到装配Bean的目的。其中使用setter方法装配Bean是最为常用的。
先说一下setter方法的装配:setter方法可以装配基本类型、Bean类型、内部bean类型、集合类型、设置空值
所谓Bean类型属性是指这个属性类型是个javaBean,并且这个javaBean必须已经在Spring中注册过了!
对于基本数据类型,可以包括八种基本类型以及其包装类
<property name="" value="" />就可以了,不用在意具体类型,Spring会自动转换的。
装配基本类型:包括八种基本类型及它们的包装类,还有String类型。不必关心具体类型,spring可以通过反射机制了解到属性的类型信息可以通过<property>的子元素<value>来设置基本类型的值,也可以通过其属性value来设置,效果完全相同。
<bean id="first" class="mybeans.FirstBean" > <property name="message"> <value>Hi, Rod Johnson</value> </property> <property name="age" value="1" /> //注意不能同时使用value子元素和value属性</bean>
--------------------------------------------------------------------------
装配bean类型属性:只有通过配置文件向容器注册的bean才能通过注入机制设置到其它bean的属性上。使用<ref>子元素或ref属性设置Spring即是通过这种方式建立起bean间的依赖关系,实现强大的bean管理功能。
举例说明:BasicDataSource
引入包commons-dbcp、commons-pool以及commons-collections三个包引入还有mysql的驱动程序将BasicDataSource中的setter方法注入Spring中去。
在bean.xml中://将BasicDataSource中的setter方法中的对应值设置进去!<beans> <bean id="data" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///j2ee"/> <property name="username" value="root" /> <property name="password" value="root" /> </bean> <bean id="course" class="bean.CourseBean"> <property name="dataSource" ref="data"/> //由于是bean,所以使用ref! </bean></beans>
建立一个Bean,对应数据库中的一个表public class CourseBean{ private int id ; private String name ; private DataSource dataSource ; //加入getter和setter方法
public void save() { }}
然后就可以进行一下数据库的操作了……
在main函数中执行下面的代码:---------------------------------------------------------------Resource res = new ClassPathResource("bean.xml");XmlBeanFactory factory = new XmlBeanFactory(res);CourseBean course = (CourseBean)factory.getBean("course");//由于数据源和courseBean之间的依赖关系已经通过配置文件给配置好了,所以这里//只需要得到courseBean,就自然有了数据源的bean了。course.setName("adsf");course.save();-----------------------------------------------------------------所谓DAO就是既有值,又可以将这个值存入数据库的对象,也就是数据库访问对象Data Access Object
所谓VO就是仅仅有值,也就是value object,值对象。上面的这个course就是DAO了。
上面的save方法可以简单的写成下面的样子:
public void save() { try { Connection con = this.dataSource.getConnection(); String sql = "insert into courses (coursename,period)"+ "values(rtrim(?),rtrim(?))" ; PreparedStatement pstmt = con.prepareStatement(sql) ; pstmt.setString(1,this.coursename); pstmt.setInt(2,this.period.intValue()); pstmt.executeUpdate(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
======================================================================
如果一个bean在使用前,其某个属性的值必须已经通过注入机制注入,spring可以协助检查这个属性是否被初始化。bean的属性dependency-check即用来实现这样的功能,其可选值包括none --不检查objects --仅检查bean类型simple --仅检查基本类型(8种)和String类型all --全检查default --默认,不检查
此外,因为可能会调用其它对象的方法,有时一个对象在使用前必须要求另外一个对象已经初始化完成这种依赖关系可以通过depends-on属性来实现,spring会保证在初始化当前对象前将depends-on指定的对象也初始化出来。
如果依赖多个对象,使用逗号将它们的名字分开。
内部Bean<bean id="outer" class="..."> <property name="target"> <bean class="com.mycompany.PersonImpl"> <property name="name"> <value>Tony</value> </property> </bean> </property></bean>
----------------------------------------------------Spring支持以下四种集合的装配:<list>--java.util.List<set>--java.util.Set<map>--java.util.Map<props>--java.util.Properties
list:<property name="someList"> <list> <value>someValue</value> <ref bean="myDataSource"/> <list> <value>anyValue</value> </list> </list></property>
set:<property name="someSet"> <set> <value>just some string</value> <ref bean="myDataSource"/> </set></property>
map:<property name="someMap"> <map> <entry> <key><value>yup an entry</value></key> <value>just some string</value> </entry> <entry> <key><value>yup a ref</value></key> <ref bean="myDataSource"/> </entry> </map></property>
props:<property name="people"> <props> <prop key="HarryPotter"> The magic property </prop> <prop key="JerrySeinfeld"> The funny property </prop> </props></property>====================================================================================================================
如果属性没有setter方法的话,需要考虑使用构造方法装配pbean的属性,这就是构造装配一般在以下情况使用:
属性没有setter方法,是只读属性属性只需要设置一次,以后就不会再更改一般使用构造装配只装配少量属性
注意在构造装配下,构造函数中应该包含有需要装配的属性
比如:<bean id="i" class="java.lang.Integer"> <constructor-arg value="23" /></bean>
这就是利用构造函数将Integer注入到容器中去。
Integer i = (Integer)factory.getBean("i");System.out.println(i);
-----------------------------------------------------<bean id="first" class="mybeans.FirstBean"> <constructor-arg> <value>http://www.sohu.com</value> </constructor-arg> <constructor-arg> <value>http://localhost:8080</value> </constructor-arg> <property name="age" value="1" /></bean>多个参数时使用多个constructor-arg指定,出现次序决定了参数次序,或者使用index属性指定
======================================================
自动装配:
可以通过spring框架自动为bean装配属性。自动装配只能装配bean类型,即取代ref元素。使用自动装配只需在bean元素中加入属性autowire,自动装配可被手动装配覆盖。也可以选择在beans中加入default-autowire属性,为所有bean设置默认自动装配。
“no“-不使用自动装配,spring推荐。“byName”-依据名称或ID装配bean,如果没有找到,则不装配,可依赖dependency-check属性检查是否装配。 “byType”-依据类型自动装配,如果存在两个以上同种类型,则抛出异常。“constructor”-依据当前bean构造函数装配,查找与构造函数参数同类型bean“autodetect”-自动检测,先通过constructor,再使用byType
Spring是不推荐使用自动装配的,首先是系统开销的问题,还有就是同名的设置可能会导致意外的问题……
====================================================================
通过BeanFactory获取Bean与直接new一个Bean是完全不同的。不相同的地方在于:从factory中得到的bean是被注入依赖关系的bean,而new出来的bean则没有依赖关系,属性是空的。
通过BeanFactory获取的bean能将所有依赖注入到bean中,属性的值会依设置而定。New出来的bean属性值必须主动设置。在需要使用spring框架情况下,所有bean都应该由容器来管理。
===================================================================
单例和原型的问题:依赖于singleton参数,true为单例模式。延迟加载:体现在整个容器初始化的时候立刻加载bean还是等到使用这个bean的时候才加载。 factory.getBean的时候就已经是开始使用bean了,并不一定要访问其中的属性 才算是使用。
只有ApplicationContext才能做到容器初始化的时候立刻加载bean,BeanFactory 是做不到的。
注册bean的时候,name属性是用的别名,id属性标明真名。name一般是在web开发里面去用,name里面是可以包含一些特殊符号的,用于告诉开发者当前的属性代表什么具体的实际意思。
setter方法、构造函数和自动装配三种装配bean的方式
=====================================================================
ApplicationContext:
ApplicationContext与BeanFactory的作用相类似,都起到装配、管理Bean的作用。不同的是,ApplicationContext在此基础上增强了BeanFactory的功能,这包括国际化、加载资源、发布事件等等。
国际化的支持:
Spring提供了两个实现了MessageSource接口的类,它们都在org.springframework.context.support包中:
ResourceBundleMessageSource使用JFC中标准的java.util.ResourceBundle来操作资源ReloadableResourceBundleMessageSource 可以在不重启虚拟机的情况下重新加载资源。
ResourceBundleMessageSource是具体的类而且有setter方法,所以可以注入根据setBasename,<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename" value="bean.res" /></bean>
千万注意一点,只要是资源包的话,id必须起名字为“messageSource”!!!!
这里还需要在bean包下面建立一个资源包res.properties,在里面写上:msg=Hello,{0}
public static void main(String[] args){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); String s = context.getMessage("msg",new Object[]{"Spring-Hibernate"},new Locale("zh")); System.out.println(s);}
这就打印出了“Hello,Spring-Hibernate”
------------------------------------------假如有多个资源包呢?比如我又在bean包里面建了一个ress.propertiesm=M
那么<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>bean.res</value> <value>bean.ress</value> </list> </property></bean>
--------------------------------------------------------
可以通过IOC机制加载资源,如<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename"> <value>springResource</value> </property></bean>或者<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basenames"> <list> <value>format</value> <value>exceptions</value> <value>windows</value> </list> </property></bean>
千万注意一点,只要是资源包的话,id必须起名字为“messageSource”!!!!
