spring其实就是一个大型的工厂,而Spring容器中的Bean就是该工厂的产品。对于我们而言,我们使用Spring框架所做的就是两件事:开发Bean、配置Bean。对于Spring来说,它要做的就是根据配置文件来创建Bean实例,并调用Bean实例的方法完成“依赖注入”。
Bean的定义
< beans…/>元素是Spring配置文件的根元素,< bean…/>元素师< beans../>元素的子元素,< beans…/>元素可以包含多个< bean…/>子元素,每个< bean…/>元素可以定义一个Bean实例,每一个Bean对应Spring容器里的一个Java实例定义Bean时通常需要指定两个属性。
- ID : 确定该Bean的唯一标识符,容器对Bean管理、访问、以及该Bean的依赖关系,都通过该属性完成。Bean的id属性在Spring容器中是唯一的。
- Class : 指定该Bean的具体实现类。注意这里不能使接口。通常情况下,Spring会直接使用new关键字创建该Bean的实例,因此,这里必须提供Bean实现类的类名。
下面是定义一个Bean的简单配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- 定义第一个Bean实例:bean1 --> <bean id="bean1" class="com.Bean1" /> <!-- 定义第二个Bean实例:bean2 --> <bean id="bean2" class="com.Bean2" /> </bean>
Spring容器集中管理Bean的实例化,Bean实例可以通过BeanFactory的getBean(Stringbeanid)方法得到。BeanFactory是一个工厂,程序只需要获取BeanFactory引用,即可获得Spring容器管理全部实例的引用。程序不需要与具体实例的实现过程耦合。大部分Java EE应用里,应用在启动时,会自动创建Spring容器,组件之间直接以依赖注入的方式耦合,甚至无须主动访问Spring容器本身。
bean的创建方式
利用无参构造函数+setter方法注入值
最基本的对象创建方式,只需要有一个无参构造函数(类中没有写任何的构造函数,默认就是有一个构造函数,如果写了任何一个构造函数,默认的无参构造函数就不会自动创建哦!!)和字段的setter方法。
package com.heqing.spring.bean; public class Person { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person [name=" + name + ", id=" + id + "]"; } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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.xsd"> < bean class="com.heqing.spring.bean.Person" id="person"> <property name="name" value="heqing"></property> <property name="id" value="123"></property> </bean> </beans>
利用有参构造函数直接注入
package com.heqing.spring.bean; public class Person { private Integer id; private String name; public Person(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person [name=" + name + ", id=" + id + "]"; } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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.xsd"> < bean class="com.heqing.spring.bean.Person" id="person"> <constructor-arg name="id" value="123"></constructor-arg> <constructor-arg name="name" value="heqing"></constructor-arg> </bean> </beans>
利用静态工厂方法注入
package com.heqing.spring.factory; import com.heqing.spring.bean.Person; public class PersonStaticFactory { public static Person createPerson(){ return new Person(); } /** * 工厂方法带有参数如何处理 * @Title: createPerson * @Description: TODO(这里用一句话描述这个方法的作用) * @param @param id * @param @param name * @return Person 返回类型 */ public static Person createPerson(Integer id,String name){ return new Person(name,id); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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.xsd"> <!--静态的工厂方法核心是class+factory-method --> <bean id="person" class="com.mc.base.learn.spring.factory.PersonStaticFactory" factory-method="createPerson"></bean> </beans>
利用工厂方法注入
package com.heqing.spring.factory; import com.heqing.spring.bean.Person; public class PersonFactory { public Person createInstance() { return new Person(); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" 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.xsd"> <!-- 实例工程方法需要先创建工厂实例,然后在创建所需对象的时候,将其赋值为factory-bean --> <bean id="personFactory" class="com.mc.base.learn.spring.factory.PersonFactory"></bean> <bean id="person2" factory-bean="personFactory" factory-method="createInstance"></bean> </beans>
Bean的作用域
当通过spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。Spring支持如下5种作用域:
singleton
单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例。并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
<bean id="empServiceImpl" class="cn.heqing.service.EmpServiceImpl" scope="singleton">
prototype
原型模式,prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
<bean id="empServiceImpl" class="cn.heqing.service.EmpServiceImpl" scope="prototype">
request
对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态, 而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。 当处理请求结束,request作用域的bean实例将被销毁。。只有在Web应用中使用Spring时,该作用域才有效。
<bean id="loginAction" class="cn.heqing.Action.loginAction" scope="request">
session
对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例。同样只有在Web应用中使用Spring时,该作用域才有效。针对某个HTTP Session,Spring容器会根据loginAction bean定义创建一个全新的loginAction bean实例, 且该loginAction bean仅在当前HTTP Session内有效。 与request作用域一样,你可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据loginAction创建的实例, 将不会看到这些特定于某个HTTP Session的状态变化。 当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
<bean id="loginAction" class="cn.heqing.Action.loginAction" scope="session">
globalsession
每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。
<bean id="empServiceImpl" class="cn.heqing.service.EmpServiceImpl" scope="globalSession">
其中比较常用的是singleton和prototype两种作用域。对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。在这种情况下,Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。
如果不指定Bean的作用域,Spring默认使用singleton作用域。Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。
Bean 的生命周期
Spring Bean的完整生命周期从创建Spring容器开始,直到最终Spring容器销毁Bean,这其中包含了一系列关键点。
生命周期流程图
各种接口方法分类
Bean自身的方法:这个包括了Bean本身调用的方法和通过配置文件中的init-method和destroy-method指定的方法。
Bean级生命周期接口方法:这个包括了BeanNameAware、BeanFactoryAware、InitializingBean和DiposableBean这些接口的方法。
容器级生命周期接口方法:这个包括了InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现,一般称它们的实现类为“后处理器”。
工厂后处理器接口方法:这个包括了AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer等等非常有用的工厂后处理器 接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。
Bean 的依赖注入
注入常量
public class HelloImpl implements HelloApi { private String message; private int index; private boolean success; private Test test; //setter方法 public void setMessage(String message) { this.message = message; } public void setIndex(int index) { this.index = index; } public void setSuccess(boolean success) { this.success = success; } public void setTest (Test test) { this.test= test; } @Override public void sayHello() { System.out.println(index + ":" + message); } }
<bean id="bean1" class="com.heqing.spring.bean.HelloImpl"> <property name="message" value="Hello World!"/> <property name="index"><value>1</value></property> </bean> <!-- boolean参数值可以用on/off --> <bean id="bean2" class="com.heqing.spring.bean.HelloImpl"> <property name="success" value="on"/> </bean> <!-- boolean参数值可以用yes/no --> <bean id="bean3" class="com.heqing.spring.bean.HelloImpl"> <property name="success" value="yes"/> </bean> <!-- boolean参数值可以用1/0 --> <bean id="bean4" class="com.heqing.spring.bean.HelloImpl"> <property name="success" value="1"/> </bean>
注入Bean ID
<bean id="test1" class="com.heqing.spring.bean.Test" /> <bean id="bean1" class="com.heqing.spring.bean.HelloImpl"> <property name="test"><idref bean="test1"/></property> </bean> <bean id="bean2" class="com.heqing.spring.bean.HelloImpl"> <property name="test"><idref local="test1"/></property> </bean>
第二种方式()可以在容器初始化时校验被引用的Bean是否存在,如果不存在将抛出异常,而第一种方式()只有在Bean实际使用时才能发现传入的Bean的ID是否正确,可能发生不可预料的错误。因此如果想注入Bean的ID,推荐使用第二种方式。
注入List类型
import java.util.List; public class ListTestBean { private List<String> values; public List<String> getValues() { return values; } public void setValues(List<String> values) { this.values = values; } }
<bean id="listBean" class="com.heqing.spring.bean.SetTestBean"> <property name="values"> <list> <value>1</value> <value>2</value> <value>3</value> </list> </property> </bean>
注入Set类型
import java.util.Collection; public class CollectionTestBean { private Collection<String> values; public void setValues(Collection<String> values) { this.values = values; } public Collection<String> getValues() { return values; } }
<bean id="setBean" class="com.heqing.spring.bean.SetTestBean"> <property name="values"> <set> <value>1</value> <value>2</value> <value>3</value> </set> </property> </bean>
注入数组类型
注入字典(Map)类型
注入Properties
构造器注入方式
setter注入方式
内部Bean定义
<bean id="bean" class="com.heqing.spring.bean.HelloImpl "> <property name="test"> <bean id="test1" class="com.heqing.spring.helloworld.Test"/> </property> </bean>
Bean的自动装配
自动装配就是指由Spring来自动地注入依赖对象,无需人工参与。
目前Spring3.0支持“no”、“byName ”、“byType”、“constructor”四种自动装配,默认是“no”指不支持自动装配的,其中Spring3.0已不推荐使用之前版本的“autodetect”自动装配,推荐使用Java 5+支持的(@Autowired)注解方式代替;如果想支持“autodetect”自动装配,请将schema改为“spring-beans-2.5.xsd”或去掉。
自动装配的好处是减少构造器注入和setter注入配置,减少配置文件的长度。自动装配通过配置标签的“autowire”属性来改变自动装配方式。接下来让我们挨着看下配置的含义。
no
意思是不支持自动装配,必须明确指定依赖。
<bean id="helloApi" class="com.heqing.spring.server.HelloImpl" autowire="no"/>
byName
意思是根据名字进行自动装配,只能用于setter注入。比如我们有方法“setHelloApi”,则“byName”方式Spring容器将查找名字为helloApi的Bean并注入,如果找不到指定的Bean,将什么也不注入。
<bean id="helloApi" class="com.heqing.spring.server.HelloImpl" autowire="byName"/>
byType
意思是指根据类型注入,用于setter注入。如果找到一个则注入该Bean,如果找不到将什么也不注入,如果找到多个Bean将优先注入标签“primary”属性为true的Bean,否则抛出异常来表明有个多个Bean发现但不知道使用哪个。
<bean id="helloApi" class="com.heqing.spring.server.HelloImpl" autowire="byType"/>
constructor
功能和“byType”功能一样,根据类型注入构造器参数,只是用于构造器注入方式。
<bean id="helloApi" class="com.heqing.spring.server.HelloImpl" autowire="constructor"/>
不是所有类型都能自动装配
- 不能自动装配的数据类型:Object、基本数据类型(Date、CharSequence、Number、URI、URL、Class、int)等;
- 通过“”标签default-autowire-candidates属性指定的匹配模式,不匹配的将不能作为自动装配的候选者,例如指定“*Service,*Dao”,将只把匹配这些模式的Bean作为候选者,而不匹配的不会作为候选者;
- 不通过将“”标签的autowire-candidate属性可被设为false,从而该Bean将不会作为依赖注入的候选者。
数组、集合、字典类型的根据类型自动装配和普通类型的自动装配是有区别的
- 数组类型、集合(Set、Collection、List)接口类型 :将根据泛型获取匹配的所有候选者并注入到数组或集合中,如“List< HelloApi> list”将选择所有的HelloApi类型Bean并注入到list中,而对于集合的具体类型将只选择一个候选者,“如 ArrayList< HelloApi> list”将选择一个类型为ArrayList的Bean注入,而不是选择所有的HelloApi类型Bean进行注入;
- 字典(Map)接口类型 :同样根据泛型信息注入,键必须为String类型的Bean名字,值根据泛型信息获取,如“Map< String, HelloApi> map” 将选择所有的HelloApi类型Bean并注入到map中,而对于具体字典类型如“HashMap< String, HelloApi> map”将只选择类型为HashMap的Bean注入,而不是选择所有的HelloApi类型Bean进行注入。
Bean的依赖检查
用于检查Bean定义的属性都注入数据了,不管是自动装配的还是配置方式注入的都能检查,如果没有注入数据将报错,从而提前发现注入错误,只检查具有setter方法的属性。
Spring3+也不推荐配置方式依赖检查了,建议采用Java5+ @Required注解方式,测试时请将XML schema降低为2.5版本的,和自动装配中“autodetect”配置方式的xsd一样。
依赖检查有none、simple、object、all四种方式.
- none
默认方式,表示不检查;
<bean id="helloApi" class="com.heqing.spring.server.HelloImpl"/>
objects
检查除基本类型外的依赖对象,配置方式为:dependency-check=”objects”,为一个String类型属性“message”,如果有简单数据类型的属性为null,也不报错;
<bean id="helloApi" class="com.heqing.spring.server.HelloImpl" dependency-check="objects"/>
simple
对基本类型进行依赖检查,包括数组类型,其他依赖不报错;配置方式为:dependency-check=”simple”,以下配置中没有注入message属性,所以会抛出异常
<bean id="helloApi" class="com.heqing.spring.server.HelloImpl" dependency-check="simple"/>
all
对所以类型进行依赖检查,配置方式为:dependency-check=”all”,如下配置方式中如果两个属性其中一个没配置将报错。
<bean id="helloApi" class="com.heqing.spring.server.HelloImpl" dependency-check="all"/>
延迟初始化Bean
延迟初始化也叫做惰性初始化,指不提前初始化Bean,而是只有在真正使用时才创建及初始化Bean。
配置方式很简单只需在标签上指定 “lazy-init” 属性值为“true”即可延迟初始化Bean。Spring容器会在创建容器时提前初始化“singleton”作用域的Bean,“singleton”就是单例的意思即整个容器每个Bean只有一个实例,后边会详细介绍。Spring容器预先初始化Bean通常能帮助我们提前发现配置错误,所以如果没有什么情况建议开启,除非有某个Bean可能需要加载很大资源,而且很可能在整个应用程序生命周期中很可能使用不到,可以设置为延迟初始化。
延迟初始化的Bean通常会在第一次使用时被初始化;或者在被非延迟初始化Bean作为依赖对象注入时在会随着初始化该Bean时被初始化,因为在这时使用了延迟初始化Bean。
容器管理初始化Bean消除了编程实现延迟初始化,完全由容器控制,只需在需要延迟初始化的Bean定义上配置即可,比编程方式更简单,而且是无侵入代码的。
<bean id="helloApi" class="com.heqing.spring.server.HelloImpl" lazy-init="true"/>