SpringBean ⼯作原理详解
本⽂Github 开源项⽬,只供⾃⼰学习总结⽆商业⽤途,如有侵权,联系删除
前⾔
在 Spring 中,那些组成应⽤程序的主体及由 Spring IOC 容器所管理的对象,被称之为 bean 。简单地讲,bean 就是由 IOC 容器初始化、装配及管理的对象,除此之外,bean 就与应⽤程序中的其他对象没有什么区别了。⽽ bean
的定义以及 bean 相互间的依赖关系将通过配置元数据来描述。
Spring 中的bean 默认都是单例的,这些单例Bean 在多线程程序下如何保证线程安全呢? 例如对于Web 应⽤来说,Web 容器对于每个⽤户请求都创建⼀个单独的Sevlet 线程来处理请求,引⼊Spring 框架之后,每个Action 都是单例
的,那么对于Spring 托管的单例Service Bean ,如何保证其安全呢? Spring 的单例是基于BeanFactory 也就是Spring 容器的,单例Bean 在此容器内只有⼀个,Java 的单例是基于 JVM ,每个 JVM 内只有⼀个实例。
bean 的作⽤域
创建⼀个bean 定义,其实质是⽤该bean 定义对应的类来创建真正实例的“配⽅”。把bean 定义看成⼀个配⽅很有意义,它与class 很类似,只根据⼀张“处⽅”就可以创建多个实例。不仅可以控制注⼊到对象中的各种依赖和配置值,还
可以控制该对象的作⽤域。这样可以灵活选择所建对象的作⽤域,⽽不必在Java Class 级定义作⽤域。Spring Framework ⽀持五种作⽤域,分别阐述如下表。
五种作⽤域中,request 、session 和 global session 三种作⽤域仅在基于web 的应⽤中使⽤(不必关⼼你所采⽤的是什么web 应⽤框架),只能⽤在基于 web 的 Spring ApplicationContext 环境。
singleton ,当⼀个 bean 的作⽤域为 singleton ,那么Spring IoC 容器中只会存在⼀个共享的 bean 实例,并且所有对 bean 的请求,只要 id 与该 bean 定义相匹配,则只会返回bean 的同⼀实例。 singleton 是单例类型(对应于单例模式),就是在创建起容器时就同时⾃动创建了⼀个bean 的对象,不管你是否使⽤,但我们可以指定Bean 节点的  来延迟初始化bean ,这时候,只有在第⼀次获取bean 时才会初始化bean ,即第⼀次
请求该bean 时才初始化。 每次获取到的对象都是同⼀个对象。注意,singleton 作⽤域是Spring 中的缺省作⽤域。要在XML 中将 bean 定义成 singleton ,可以这样配置:
也可以通过  注解(它可以显⽰指定bean 的作⽤范围。)的⽅式
prototype ,当⼀个bean 的作⽤域为 prototype ,表⽰⼀个 bean 定义对应多个对象实例。prototype 作⽤域的 bean 会导致在每次对该 bean 请求(将其注⼊到另⼀个 bean 中,或者以程
序的⽅式调⽤容器的 getBean() ⽅法)时都会创建⼀个新的 bean 实例。prototype 是原型类型,它在我们创建容器的时候并没有实例化,⽽是当我们获取bean 的时候才会去创建⼀个对象,⽽且我们每次获取到的对象都不是同⼀个对
象。根据经验,对有状态的 bean 应该使⽤ prototype 作⽤域,⽽对⽆状态的 bean 则应该使⽤ singleton 作⽤域。 在 XML 中将 bean 定义成 prototype ,可以这样配置:
通过  注解的⽅式实现就不做演⽰了。
request
,request 只适⽤于Web 程序,每⼀次 HTTP 请求都会产⽣⼀个新的bean ,同时该bean 仅在当前HTTP request 内有效,当请求
结束后,该对象的⽣命周期即告结束。 在 XML 中将 bean 定义成 request ,可以这样配置:
session
,session 只适⽤于Web 程序,session 作⽤域表⽰该针对每⼀次 HTTP 请求都会产⽣⼀个新的 bean ,同时该 bean 仅在当前HTTP session 内有效.与request 作⽤域⼀样,可以根据需要放⼼的更改所创建实例的内部状态,⽽别的 HTTP session 中根据 userPreferences 创建的实例,将不会看到这些特定于某个 HTTP session 的状态变化。当HTTP session 最终
被废弃的时候,在该HTTP session 作⽤域内的bean 也会被废弃掉。
globalSession
global session 作⽤域类似于标准的 HTTP session 作⽤域,不过仅仅在基于 portlet 的 web 应⽤中才有意义。Portlet 规范定义了全局 Session 的概念,它被所有构成某个 portlet web 应⽤的各种不同的 portle t 所共享。在global
session 作⽤域中定义的 bean 被限定于全局portlet Session 的⽣命周期范围内。
bean 的⽣命周期
Spring Bean 是Spring 应⽤中最最重要的部分了。所以来看看Spring 容器在初始化⼀个bean 的时候会做那些事情,顺序是怎样的,在容器关闭的时候,⼜会做哪些事情。
spring 版本:4.2.3.RELEASE
鉴于Spring 源码是⽤gradle 构建的,我也决定舍弃我⼤maven ,尝试下洪菊推荐过的gradle 。运⾏beanLifeCycle 模块下的junit test 即可在控制台看到如下输出,可以清楚了解Spring 容器在创建,初始化和销毁Bean
的时候依次做了那些事情。
类别
说明singleton
在Spring IoC 容器中仅存在⼀个Bean 实例,Bean 以单例⽅式存在,默认值prototype
每次从容器中调⽤Bean 时,都返回⼀个新的实例,即每次调⽤getBean()时,相当于执⾏new XxxBean()request
每次HTTP 请求都会创建⼀个新的Bean ,该该作⽤域仅适⽤于WebApplicationContext 环境session
同⼀个HTTP Session 共享⼀个Bean ,不同Session 使⽤不同Bean ,仅适⽤于WebApplicationContext 环境globalSession ⼀般⽤于Portlet 应⽤环境,该作⽤域仅适⽤于WebApplicationContext 环境
singleton 唯⼀ bean 实例lazy-init=”true”<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">
@Scope @Service
@Scope("singleton")
public class ServiceImpl{
}
prototype 每次请求都会创建⼀个新的 bean 实例<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>
或者
<bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
@Scope request 每⼀次HTTP 请求都会产⽣⼀个新的bean ,该bean 仅在当前HTTP request 内有效<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
session 每⼀次HTTP 请求都会产⽣⼀个新的 bean ,该bean 仅在当前 HTTP session 内有效<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
<bean id="user" class="com.foo.Preferences "scope="globalSession"/>
Spring 容器初始化
=====================================
调⽤GiraffeService ⽆参构造函数
GiraffeService 中利⽤set ⽅法设置属性值
调⽤setBeanName:: Bean Name defined in context=giraffeService
调⽤setBeanClassLoader,ClassLoader Name = sun.misc.Launcher$AppClassLoader
调⽤setBeanFactory,setBeanFactory:: giraffe bean singleton=true
调⽤setEnvironment
调⽤setResourceLoader:: Resource File l
调⽤setApplicationEventPublisher
调⽤setApplicationContext:: Bean Definition Names=[giraffeService, t.annotation.CommonAnnotationBeanPostProcessor#0, com.giraffe.spring.service.GiraffeServicePostProcessor#0]执⾏BeanPostProcessor 的postProcessBeforeInitialization ⽅法,beanName=giraffeService
调⽤PostConstruct 注解标注的⽅法
执⾏InitializingBean 接⼝的afterPropertiesSet ⽅法
session和application的区别
执⾏配置的init-method
执⾏BeanPostProcessor 的postProcessAfterInitialization ⽅法,beanName=giraffeService
Spring 容器初始化完毕
=====================================
从容器中获取Bean
giraffe Name=李光洙
=====================================
调⽤preDestroy 注解标注的⽅法
执⾏DisposableBean 接⼝的destroy ⽅法
先来看看,Spring 在Bean 从创建到销毁的⽣命周期中可能做得事情。
initialization 和 destroy
有时我们需要在Bean 属性值set 好之后和Bean 销毁之前做⼀些事情,⽐如检查Bean 中某个属性是否被正常的设置好值了。Spring 框架提供了多种⽅法让我们可以在Spring Bean 的⽣命周期中执⾏initialization 和pre-destroy ⽅法。
1.实现InitializingBean 和DisposableBean 接⼝
这两个接⼝都只包含⼀个⽅法。通过实现InitializingBean 接⼝的afterPropertiesSet()⽅法可以在Bean 属性值设置好之后做⼀些操作,实现DisposableBean 接⼝的destroy()⽅法可以在销毁Bean 之前做⼀些操作。
例⼦如下:
这种⽅法⽐较简单,但是不建议使⽤。因为这样会将Bean 的实现和Spring 框架耦合在⼀起。
2.在bean 的配置⽂件中指定init-method 和destroy-method ⽅法
Spring 允许我们创建⾃⼰的 init ⽅法和 destroy ⽅法,只要在 Bean 的配置⽂件中指定 init-method 和 destroy-method 的值就可以在 Bean 初始化时和销毁之前执⾏⼀些操作。
例⼦如下:
配置⽂件中的配置:
需要注意的是⾃定义的init-method 和post-method ⽅法可以抛异常但是不能有参数。
这种⽅式⽐较推荐,因为可以⾃⼰创建⽅法,⽆需将Bean 的实现直接依赖于spring 的框架。
3.使⽤@PostConstruct 和@PreDestroy 注解
除了xml 配置的⽅式,Spring 也⽀持⽤ 和 注解来指定  和  ⽅法。这两个注解均在 包中。为了注解可以⽣效,需要在配置⽂件中定义
t.annotation.CommonAnnotationBeanPostProcessor 或context:annotation-config
例⼦如下:
配置⽂件:
实现XxxAware 接⼝ 在Bean 中使⽤Spring 框架的⼀些对象,有些时候我们需要在 Bean 的初始化中使⽤ Spring 框架⾃⾝的⼀些对象来执⾏⼀些操作,⽐如获取 ServletContext 的⼀些参数,获取 ApplicaitionContext 中的BeanDefinition 的名字,获取 Bean 在容器中的名字等等。为了让 Bean 可以获取到框架⾃⾝的⼀些对象,Spring 提供了⼀组名为*Aware 的接⼝。
这些接⼝均继承于标记接⼝,并提供⼀个将由 Bean 实现的set*⽅法,Spring 通过基于setter 的依赖注⼊⽅式使相应的对象可以被Bean 使⽤。
⽹上说,这些接⼝是利⽤观察者模式实现的,类似于servlet listeners ,⽬前还不明⽩,不过这也不在本⽂的讨论范围内。
介绍⼀些重要的Aware 接⼝:
ApplicationContextAware: 获得ApplicationContext 对象,可以⽤来获取所有Bean definition 的名字。
BeanFactoryAware:获得BeanFactory 对象,可以⽤来检测Bean 的作⽤域。
BeanNameAware:获得Bean 在配置⽂件中定义的名字。
ResourceLoaderAware:获得ResourceLoader 对象,可以获得classpath 中某个⽂件。
ServletContextAware:在⼀个MVC 应⽤中可以获取ServletContext 对象,可以读取context 中的参数。ServletConfigAware : 在⼀个MVC 应⽤中可以获取ServletConfig 对象,可以读取config 中的参数。
执⾏DisposableBean 接⼝的destroy ⽅法
执⾏配置的destroy-method
Spring 容器关闭
public class GiraffeService implements InitializingBean,DisposableBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("执⾏InitializingBean 接⼝的afterPropertiesSet ⽅法");
}
@Override
public void destroy() throws Exception {
System.out.println("执⾏DisposableBean 接⼝的destroy ⽅法");
}
}
public class GiraffeService {
//通过<bean>的destroy-method 属性指定的销毁⽅法
public void destroyMethod() throws Exception {
System.out.println("执⾏配置的destroy-method");
}
//通过<bean>的init-method 属性指定的初始化⽅法
public void initMethod() throws Exception {
System.out.println("执⾏配置的init-method");
}
}
<bean name="giraffeService" class="com.giraffe.spring.service.GiraffeService" init-method="initMethod" destroy-method="destroyMethod">
</bean>
@PostConstruct @PreDestroy init destroy javax.annotation public class GiraffeService {
@PostConstruct
public void initPostConstruct(){
System.out.println("执⾏PostConstruct 注解标注的⽅法");
}
@PreDestroy
public void preDestroy(){
System.out.println("执⾏preDestroy 注解标注的⽅法");
}
}
<bean class="t.annotation.CommonAnnotationBeanPostProcessor" />
实现XxxAware 接⼝ 在Bean 中使⽤Spring 框架的⼀些对象org.springframework.beans.factory.Aware public class GiraffeService implements  ApplicationContextAware,
ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware,
BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware{
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("执⾏setBeanClassLoader,ClassLoader Name = " + Class().getName());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("执⾏setBeanFactory,setBeanFactory:: giraffe bean singleton=" +  beanFactory.isSingleton("giraffeService"));
}
@Override
public void setBeanName(String s) {
System.out.println("执⾏setBeanName:: Bean Name defined in context="
+ s);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("执⾏setApplicationContext:: Bean Definition Names="
+ BeanDefinitionNames()));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
System.out.println("执⾏setApplicationEventPublisher");
}
@Override
public void setEnvironment(Environment environment) {
System.out.println("执⾏setEnvironment");
BeanPostProcessor
上⾯的*Aware 接⼝是针对某个实现这些接⼝的Bean 定制初始化的过程,
Spring 同样可以针对容器中的所有Bean ,或者某些Bean 定制初始化过程,只需提供⼀个实现BeanPostProcessor 接⼝的类即可。 该接⼝中包含两个⽅法,postProcessBeforeInitialization 和postProcessAfterInitialization 。
postProcessBeforeInitialization ⽅法会在容器中的Bean 初始化之前执⾏, postProcessAfterInitialization ⽅法在容器中的Bean 初始化之后执⾏。
例⼦如下:
要将BeanPostProcessor 的Bean 像其他Bean ⼀样定义在配置⽂件中总结所以。。。结合第⼀节控制台输出的内容,Spring Bean 的⽣命周期是这样纸的:
Bean 容器到配置⽂件中 Spring Bean 的定义。
Bean 容器利⽤Java Reflection API 创建⼀个Bean 的实例。
如果涉及到⼀些属性值 利⽤set ⽅法设置⼀些属性值。
如果Bean 实现了BeanNameAware 接⼝,调⽤setBeanName()⽅法,传⼊Bean 的名字。
如果Bean 实现了BeanClassLoaderAware 接⼝,调⽤setBeanClassLoader()⽅法,传⼊ClassLoader 对象的实例。
如果Bean 实现了BeanFactoryAware 接⼝,调⽤setBeanClassLoader()⽅法,传⼊ClassLoader 对象的实例。
与上⾯的类似,如果实现了其他*Aware 接⼝,就调⽤相应的⽅法。
如果有和加载这个Bean 的Spring 容器相关的BeanPostProcessor 对象,执⾏postProcessBeforeInitialization()⽅法
如果Bean 实现了InitializingBean 接⼝,执⾏afterPropertiesSet()⽅法。
如果Bean 在配置⽂件中的定义包含init-method 属性,执⾏指定的⽅法。
如果有和加载这个Bean 的Spring 容器相关的BeanPostProcessor 对象,执⾏postProcessAfterInitialization()⽅法
当要销毁Bean 的时候,如果Bean 实现了DisposableBean 接⼝,执⾏destroy()⽅法。
当要销毁Bean 的时候,如果Bean 在配置⽂件中的定义包含destroy-method 属性,执⾏指定的⽅法。
⽤图表⽰⼀下(图来源:):
与之⽐较类似的中⽂版本:
其实很多时候我们并不会真的去实现上⾯说描述的那些接⼝,那么下⾯我们就除去那些接⼝,针对bea
n 的单例和⾮单例来描述下bean 的⽣命周期:
单例管理的对象
当scope=”singleton”,即默认情况下,会在启动容器时(即实例化容器时)时实例化。但我们可以指定Bean 节点的lazy-init=”true”来延迟初始化bean ,这时候,只有在第⼀次获取bean 时才会初始化bean ,即第⼀次请求该bean 时才初始化。如下配置:
如果想对所有的默认单例bean 都应⽤延迟初始化,可以在根节点beans 设置default-lazy-init 属性为true ,如下所⽰:
默认情况下,Spring 在读取 xml ⽂件的时候,就会创建对象。在创建对象的时候先调⽤构造器,然后调⽤ init-method 属性值中所指定的⽅法。对象在被销毁的时候,会调⽤ destroy-method 属性值中所指定的⽅法(例如调⽤Container.destroy()⽅法的时候)。写⼀个测试类,代码如下:
System.out.println("执⾏setEnvironment");
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
Resource resource = Resource("l");
System.out.println("执⾏setResourceLoader:: Resource File Name="
+ Filename());
}
@Override
public void setImportMetadata(AnnotationMetadata annotationMetadata) {
System.out.println("执⾏setImportMetadata");
}
}
public class CustomerBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执⾏BeanPostProcessor 的postProcessBeforeInitialization ⽅法,beanName=" + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执⾏BeanPostProcessor 的postProcessAfterInitialization ⽅法,beanName=" + beanName);
return bean;
}
}
<bean class="com.giraffe.spring.service.CustomerBeanPostProcessor"/>
<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" lazy-init="true"/>
<beans default-lazy-init="true" …>
public class LifeBean {
 l 配置如下:
测试代码:
运⾏结果:
⾮单例管理的对象当时,容器也会延迟初始化 bean ,Spring 读取xml ⽂件的时候,并不会⽴刻创建对象,⽽是在第⼀次请求该 bean 时才初始化(如调⽤getBean ⽅法时)。在第⼀次请求每⼀个 prototype 的bean 时,Spring 容器都会调⽤其构造器创建这个对象,然后调⽤属性值中所指定的⽅法。
对象销毁的时候,Spring 容器不会帮我们调⽤任何⽅法,因为是⾮单例,这个类型的对象有很多个,Spring 容器⼀旦把这个对象交给你之后,就不再管理这个对象了。
为了测试prototype bean 的⽣命周期l 配置如下:
测试程序:
运⾏结果:
可以发现,对于作⽤域为 prototype 的 bean ,其⽅法并没有被调⽤。如果 bean 的 scope 设为prototype 时,当容器关闭时, ⽅法不会被调⽤。对于 prototype 作⽤域的 bean ,有⼀点⾮常重要,那就是 Spring 不能对⼀个 prototype bean 的整个⽣命周期负责:容器在初始化、配置、装饰或者是装配完⼀个prototype 实例后,将它交给客户端,随后就对该prototype 实例不闻不问了。 不管何种作⽤域,容器都会调⽤所有对象的初始化⽣命周期回调⽅法。但对prototype ⽽⾔,任何配置好的析构⽣命周期回调⽅法都将不会被调⽤。清除prototype 作⽤域的对象并释放任何prototype bean 所持有的昂贵资源,都是客户端代码的职责(让Spring 容器释放被prototype 作⽤域bean 占⽤资源的⼀种可⾏⽅式是,通过使⽤bean 的后置处理器,该处理器持有要被清除的bean 的引⽤)。谈及prototype 作⽤域的bean 时,在某些⽅⾯你可以将Spring 容器的⾓⾊看作是Java new 操作的替代者,任何迟于该时间点的⽣命周期事宜都得交由客户端来处理。
Spring 容器可以管理 singleton 作⽤域下 bean 的⽣命周期,在此作⽤域下,Spring 能够精确地知道bean 何时被创建,何时初始化完成,以及何时被销毁。⽽对于 prototype 作⽤域的bean ,Spring 只负责创建,当容器创建了bean 的实例后,bean 的实例就交给了客户端的代码管理,Spring 容器将不再跟踪其⽣命周期,并且不会管理那些被配置成prototype 作⽤域的bean 的⽣命周期。
说明
本⽂的完成结合了下⾯两篇⽂章,并做了相应修改:
由于本⽂⾮本⼈独⽴原创,所以未声明为原创!在此说明!public class LifeBean {
private String name;
public LifeBean(){
System.out.println("LifeBean()构造函数");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("setName()");
this.name = name;
}
public void init(){
System.out.println("this is init of lifeBean");
}
public void destory(){
System.out.println("this is destory of lifeBean " + this);
}
}
<bean id="life_singleton" class="com.bean.LifeBean" scope="singleton"
init-method="init" destroy-method="destory" lazy-init="true"/>
public class LifeTest {
@Test
public void test() {
AbstractApplicationContext container =
new ClassPathXmlApplicationContext("l");
LifeBean life1 = (Bean("life");
System.out.println(life1);
container.close();
}
}
LifeBean()构造函数
this is init of lifeBean
com.bean.LifeBean@573f2bb1
……
this is destory of lifeBean com.bean.LifeBean@573f2bb1
scope=”prototype”init-method <bean id="life_prototype" class="com.bean.LifeBean" scope="prototype" init-method="init" destroy-method="destory"/>
public class LifeTest {
@Test
public void test() {
AbstractApplicationContext container = new ClassPathXmlApplicationContext("l");
LifeBean life1 = (Bean("life_singleton");
System.out.println(life1);
LifeBean life3 = (Bean("life_prototype");
System.out.println(life3);
container.close();
}
}
LifeBean()构造函数
this is init of lifeBean
com.bean.LifeBean@573f2bb1
LifeBean()构造函数
this is init of lifeBean
com.bean.LifeBean@5ae9a829
……
this is destory of lifeBean com.bean.LifeBean@573f2bb1
destroy destroy

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。