Bean是Spring应用程序中(其实是Spring容器中)的基本元素,在Spring框架上运行的应用程序,就是由一个又一个的Bean组合在一起、像搭积木一样堆出来的。所有的Bean都由Spring的核心容器负责管理、创建、销毁,同时Bean之间的相互依赖也由Spring容器中的依赖注入功能自动管理。1.1bean的作用域
lsingleton:定义bean的范围为每个Spring容器一个实例(默认值) lprototype:定义bean可以被多次实例化(使用一次就创建一次) lrequest:定义bean的范围是HTTP请求,只有再使用有web能力的spring上下文时有效。 lsession:定义bean的范围是HTTP会话,只有再使用有web能力的spring上下文时有效。 lglobal-session:定义bean的范围是全局HTTP会话,只有再portlet上下文中有效。1.2 三种实例化bean的方式
- 使用构造器实例化(默认无参数)
<bean id="person" class="com.xiaohui.domain.Person"/>
使用静态工厂方法实例化(简单工厂模式)<bean id="person" class="com.xiaohui.app.create.PersonFactory" factory-method="getPersonInstance"/>
public class PersonFactory { public static Person getPersonInstance(){ return new Person(); } }
使用实例工厂方法实例化(工厂方法模式)<bean id="personFactory" class="com.xiaohui.app.create.PersonFactory" />
<bean id="person" factory-bean="personFactory" factory-method="createtPersonInstance"/>
public class PersonFactory{ public Person createtPersonInstance(){ return new Person(); } }
1.3 指定Bean的初始化方法和销毁方法spring初始化bean或销毁bean时有时候需要执行一些处理工作,因此spring可以在创建或者销毁bean时可以调用bean的两个生命周期方法
<bean id="personService" class="com.xiaohui.service.PersonServiceImpl"
init-method="init"
destroy-method="distory" scope="singleton" />(注意的是只有scope=singleton的bean,destroy-method才有效)
1.4 bean 的生命周期
实例化bean 设置属性值 设置bean名称(如果bean实现了BeanNameAware接口) 设置beanFactory(如果bean实现了BeanFactoryAware接口) 设置ApplicationContext(如果bean实现了ApplicationContextAware接口) 前预处理(如果上下文中存在多个BeanPostProcessor,将调用其postProcessBeforeInitialization()方法) 初始化Bean(如果bean实现了InitializingBean,将调用其afterPropertiesSet方法,如果bean声明了自定义初始化方法,调用) 后预处理(如果上下文中存在多个BeanPostProcessors,将调用其postProcessAfterInitialization()方法) Bean创建完成,待用 销毁Bean(如果bean实现了DisposableBean,调用其destroy()方法) 销毁Bean(如果bean声明了自定义的销毁方法,调用)1.5 bean的继承在spring的继承中不论在java类的实际情况中是否存在真正的extends继承关系,只要其存在引用相同的属性值,在spring的bean管理都可以将其抽象为继承关系。在spring的xml配置中可将引用相同属性的属性property抽出一个父类bean。该bean需要设计其abstract属性为true。其他“继承”该bean的bean需要制定其parent属性值为该抽象父类的id。如下在studentDao和teacherDao中都存在属性sf(SessionFactory),则可以抽出一个父类。<bean id="sessionFactory" class="com.xiaohui.test.SessionFactory" /> <bean id="baseDao" abstract="true"> <property name="sf" ref="sessionFactory" /> </bean> <bean id="studentDao" class="com.xiaohui.service.impl.StudentDaoImpl" parent="baseDao" /> <bean id="teacherDao" class="com.xiaohui.service.impl.TeacherDaoImpl" parent="baseDao" />
所谓的依赖注入就是在于行期间,由外部容器动态的将依赖对象注入到组件中。通俗的讲也就是不在程序中显示的调用该类的setter()方法去设置该类的属性。而是由spring容器处理经过程序员配置的xml来设置该类的属性值。
所谓的控制反转(Inversion of Control)就是指应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转,目的是为了获得更好的扩展性和良好的可维护性。
通常的依赖注入有手工装配(配置xml)和自动装配(基于注解)的两种方式方式:
2.1通过setter方法注入依赖
<bean>元素的<property>子元素指明了使用该bean的setter方法来注入
1.可以使用property的value属性来进行简单类型的注入
<bean id="person" class="com.xiaohui.domain.Person"> <property name="id" value="1"/> <property name="name" value="张三丰"/> </bean>2.使用ref引用其他的bean
<bean id="person" class="com.xiaohui.bean.Person" /> <bean id="personService" class="com.xiaohi.bean.impl.PersonServiceImpl"> <!-- 引用类型 --> <property name="person" ref="person" /> </bean>3.内部bean
<bean id="personService" class="com.xiaohui.bean.impl.PersonServiceImpl"> <!-- 内部bean注入 --> <property name="person"> <bean class="com.xiaohui.bean.Person" /> </propert> </bean>使用该方式的缺点就是无法在其他地方使用这个类,该类只能为外部的bean使用。
4.装配集合
<!-- 装配list --> <property name="lists"> <list> <value>list1</value> <ref bean="person" /> </list> </property> <!-- 装配数组 --> <property name="obj"> <list> <value>obj1</value> <ref bean="person" /> </list> </property> <!-- 装配set set使用方法和list一样,不同的是对象被装配到set中,而list是装到 List或数组中装配 --> <property name="sets"> <set> <value>set1</value> <value>set2</value> <ref bean="person" /> </set> </property> <!-- 装配map map中的<entry>的数值和<list>以及<set>的一样可以使任何有效的属性元素,需要注意的是key值必须是String的。 --> <property name="maps"> <map> <entry key="01" value="map01" /> <entry key="02" value="map02" /> <entry key-ref="refkeybean" value-ref="refvaluebean"> </map> </property> <property name="props"> <props> <prop key="01">prop1</prop> <prop key="02">prop2</prop> </props> </property> <!-- 简化写法 --> <property name="props"> <value> 01=prop1 02=prop2 </value> </property>5.设空值null
<!-- 装配null --> <property name="listnull"> <null/> </property>
6,自定义String2Object类型转换器来设置属性在该属性类的同一包下面创建类 名称 PropertyClassNameEditor,并继承PropertyEditorSupport类,复写其setAsText(String text)方法即可。
例如给Family类的Person类属性创建一个String2Person类的类型转换器。
public class PersonEditor extends PropertyEditorSupport { @Override public void setAsText(String text) throws IllegalArgumentException { if(text!=null){ String[] props = text.split("-"); if(props!=null && props.length == 3){ Person p = new Person(); p.setId(Long.parseLong(props[0])); p.setName(props[1]); p.setMail(props[2]); this.setValue(p); } } } }Family类:
public class Family { private Person master; public void setMaster(Person master) { this.master = master; } public void sayMaster(){ System.out.println(master); } }Person类:
public class Person { private Long id; private String name; private String mail; public String getMail() { return mail; } public void setMail(String mail) { this.mail = mail; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", mail=" + mail + "]"; } }则可以这样给Family 类注入Person类的master属性值
<bean id="family" class="com.xiaohui.domain.Family"> <property name="master" value="[email protected]"/> </bean>7,通过加载properties文件为bean属性设值需要引入context命名空间:xml配置如下:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:property-placeholder location="classpath:jdbc.properties"/> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> </beans>jdbc.properties内容如下:jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql:///spring2 jdbc.username=root jdbc.password=root2.2. 通过构造函数注入依赖
Person类:
package com.xiaohui.domain; public class Person { private Long id; private String name; private String mail; public Person() {} public Person(Long id, String name, String mail) { this.id = id; this.name = name; this.mail = mail; } public String getMail() { return mail; } public void setMail(String mail) { this.mail = mail; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + ", mail=" + mail + "]"; } }
1.可以按照参数的类型注入,如:
<bean id="person" class="com.xiaohui.domain.Person"> <constructor-arg type="Long" value="2" /> <constructor-arg type="String" value="herry" /> <constructor-arg type="String" value="[email protected]" /> </bean>当一种类型有多个的种情况下其按照构造函数的参数顺序注入。如上,其第二个constructor-arg标签注入的为Person的name属性。
2.可以按照参数的name进行注入,如:
<bean id="person" class="com.xiaohui.domain.Person"> <constructor-arg name="id" value="2" /> <constructor-arg name="name" value="herry" /> <constructor-arg name="mail" value="[email protected]" /> </bean>其中name的值为该类的构造方法的参数名。正常情况下,通过软件自动生成的构造方法其参数名和对应的属性名是一样的。
3.可以按照索引(构造函数的参数顺序)进行注入
<bean id="person" class="com.xiaohui.domain.Person"> <constructor-arg index="0" value="2" /> <constructor-arg index="1" value="herry" /> <constructor-arg index="2" value="[email protected]" /> </bean>2.3 通过自动装配的方式进行注入
通过设置bean标签中的autowire属性。
autowire的取值为:byType:按类型装配,可以根据属性的类型,在容器中寻找跟该类型匹配的bean。如果发现多个,那么将会抛出异常。如果没有找到,即属性值为null。
byName:按名称装配,可以根据属性的名称,在容器中寻找跟该属性名相同的bean,如果没有找到,即属性值为null。
constructor:与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
byType
autowire-candidate:可选值为 true|false 当autowire匹配时有多个类型的时候,使用该属性可以表明该bean是否为优先的候选bean 通常设置哪些bean不为优先候选的
<bean id="util" class="com.xiaohui.myinject.DbUtil" /> <bean id="util2" class="com.xiaohui.myinject.DbUtil" autowire-candidate="false" /> <bean id="dao" class="com.xiaohui.myinject.PersonDaoImpl" autowire="byType"/> <bean id="personService" class="com.xiaohui.myinject.PersonServiceImpl" autowire="byType"/>byName bean的id要和依赖该bean的类中的属性名一样.
<bean id="util" class="com.xiaohui.myinject.DbUtil" /> <bean id="dao" class="com.xiaohui.myinject.PersonDaoImpl" autowire="byName"/> <bean id="personService" class="com.xiaohui.myinject.PersonServiceImpl" autowire="byName"/>2.4 通过注解方式进行装配
在使用注解方式进行注入之前呢,必须现在spring配置文件applicationContext.xml中引入context命名空间
再在配置文件中加入<context:annotation-config/>标签
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:annotation-config/> <bean id="util" class="com.xiaohui.myinject2.DbUtil" /> <bean id="personDao" class="com.xiaohui.myinject2.PersonDaoImpl" /> <bean id="personService" class="com.xiaohui.myinject2.PersonServiceImpl" /> </beans>常用的注解说明:
@Autowire & @Resource & @Qualifier
@Autowire和@Resource的区别是:@Autowired 默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。相同的是:都可以直接使用在Field上或者setter方法之上。@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。@Resource可以通过设置name属性指定注入bean的名称, 但一 旦指定了 name 属性,就只能按名称装配了如果我们想使用按名称装配,可以让@Autowired结合@Qualifier注解一起使用。对应Spring配置文件中的init-method和destory-method,在bean中,可以直接使用@PostConstruct和@PreDestory标签来标记生命周期方法。public class CachingMovieLister {@PostConstructpublic void populateMovieCache() {}@PreDestroypublic void clearMovieCache() {}}相当于:<bean id="cachingMovieLister" class="CachingMovieLister" init-method="populateMovieCache" destory-method="clearMovieCache" />更为简洁的bean管理方式:classpath自动扫描在上面的配置中我们还需要在spring的xml文件中配置bean信息。spring为我们提供了另一种更为简洁的bean管理和依赖注入的的方式,成为classpath自动扫描。使用@Component,@Controller,@Service,@Repository注释我们需要被spring管理的组件类@Controller用于标注控制层组件(如struts中的action)。@Service用于标注业务层组件。@Repository用于标注数据访问组件,即DAO组件。@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。这时我们在xml文件中只需要加入<context:component-scan base-package="com.xiaohui.package"/>,这时我们就不用在xml中配置 base-package包下面的所有bean了。xml如下:<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:annotation-config/> <context:component-scan base-package="com.xiaohui.myinject2"/> </beans>java类如下:Dbutil类:package com.xiaohui.myinject2; import org.springframework.stereotype.Component; @Component public class DbUtil { public void save(){ System.out.println("save object in dbutil...."); } }PersonDaoImpl类:package com.xiaohui.myinject2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; @Repository public class PersonDaoImpl { private DbUtil util; @Autowired public void setUtil(DbUtil util) { this.util = util; } public void save(){ System.out.println("save in PersonDaoImpl...."); util.save(); } }PersonServiceImpl类:package com.xiaohui.myinject2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class PersonServiceImpl { private PersonDaoImpl dao; @Autowired public void setDao(PersonDaoImpl dao) { this.dao = dao; } public void save(){ System.out.println("PersonServiceImpl save user....."); dao.save(); } }PersonActionPersonActionPersonAction类:package com.xiaohui.myinject2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class PersonAction { private PersonServiceImpl service; @Autowired public void setService(PersonServiceImpl service) { System.out.println("save in action....."); this.service = service; } public void execute(){ service.save(); } }测试类:package com.xiaohui.app; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.xiaohui.myinject2.PersonAction; public class AppTest { @Test public void testSpring() throws Exception { ApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml"); PersonAction service = ctx.getBean(PersonAction.class); service.execute(); } }
打印结果:save in action.....
PersonServiceImpl save user.....
save in PersonDaoImpl....
save object in dbutil....