Bean Lifecycle in Spring
Posted 袁慎建@ThoughtWorks
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bean Lifecycle in Spring相关的知识,希望对你有一定的参考价值。
Bean Lifecycle in Spring SpringIoC作为Spring的特色之一,集Bean以及Bean的管理于一身,负责各种Bean的生命周期,所以作为一个学习开发者,稍微理清一下Bean的生命周期还是有一点点必要的,犹如一个原始的Web开发者对Servlet的生命周期的熟知。 在Spring中,Bean的生命周期从两个层面定义的,一个是Bean的作用域,另一个是Bean的初始化经历了一系列过程。 1.Bean的作用域: 作用域,主要从两个大方面,一个是针对Web环境和非针对Web环境的,先看看两个小例子: scope=”singleton“:<bean id="person" class="com.meritit.ysjian.spring3learning.beanlife.Person"
init-method="myInit" destroy-method="myDestory" scope="singleton"
p:id="1001" p:age="125" p:birthday="2013/03/05" />
@上面这个person Bean的作用域是单例singleton,如果scope=”singleton“不显示指定,也是默认的单例,,如果是通过BeanFactory启动IoC容器时,BeanFactory#getBean方法在调用时才实例化该Bean,将引用返回调用者,然后在缓存池中缓存该Bean实例,Spring继续负责其生命周期的管理。
scope="prototype":
<bean id="person" class="com.meritit.ysjian.spring3learning.beanlife.Person"
init-method="myInit" destroy-method="myDestory" scope="prototype"
p:id="1001" p:age="125" p:birthday="2013/03/05" />
@该Bean的作用域是prototype,容器启动后,当第一次调用时,Spring容器实例化该Bean并返回给调用者,其后续生命周期由调用者管理,Spring不对其管理,所以prototype作用域destroy-method指定的方法不会执行。
在Spring中重要的接口:BeanFactory--->ApplicationContext---->WebApplicationContext,BeanFactory和ApplicationContext在前一篇文章介绍过, WebApplicationContext:是专门为Web应用准备的,允许从相对于Web根目录的路径中装载配置文件进行初始化,从WebApplicationContext中可以获取ServletContext的引用,ServletContext存放了Web应用的上下文对象,所以可以访问Spring应用上下文。 Spring2.0在WebApplicationContext中为Bean添加了三个新的作用域:request,session,global session,类似于jsp中的request,session,application,Servlet中的HttpServletRequest,HttpSession,ServletContext,有过Web开发经验的同学不会对这几个对象和接口不熟悉,下面简单介绍一下WebApplicationContext,先看看WebApplicationContext的体系结构: WebApplicationContext是对ApplicationContext的扩展,在WebApplicationContext中定义了一个常量ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,在上下文启动的时候,WebApplicationContext实例对象以此为键放置ServletContext属性列表,Spring提供了一个工具类WebApplicationContextUtils#getWebApplicationContext(ServletContext servletContext),下面语句是这个方法的内部实现,可以获得WebApplicationContext:
WebApplicationContext wac = (WebApplicationContext) servletContext
.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
WebApplicationContext的初始化在web.xml中进行配置,一种是监听器,另一种是Servlet自启动。在前一篇有介绍。
上面简述了Bean在Web应用中的三个作用域,所以Bean一共有五种作用域,在开发中常用的是singleton和prototype。
2.BeanFactory中Bean的初始化经历了一系列过程 来看看下面的一个流程图:
上图中橘黄色标记的接口是Spring级别的接口,它们的实现类被称为”后处理器“,一般不由Bean本身实现, MyInstantiationAwareBeanPostProcessor,实现了InstantiationAwareBeanPostProcessorAdapter:
public class MyInstantiationAwareBeanPostProcessor extends
InstantiationAwareBeanPostProcessorAdapter
/**
* 1.调用者通过getBean向容器请求某个Bean时,实例化Bean前进行调用
*/
public Object postProcessBeforeInstantiation(Class beanClass,
String beanName) throws BeansException
// 1.1仅对容器中的person Bean进行处理
if ("person".equals(beanName))
System.out
.println("MyInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()...");
return null;
/**
* 2.在实例化Bean后调用,对已经实例化的对象进行"梳妆打扮"
*/
public boolean postProcessAfterInstantiation(Object bean, String beanName)
throws BeansException
// 2.1进队容器person Bean进行处理
if ("person".equals(beanName))
System.out
.println("MyInstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()...");
return true;
/**
* 3.postProcessAfterInstantiation方法对Bean进行了处理后调用,
* 将配置值设置到Bean对应的属性中
*/
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
PropertyDescriptor[] pds, Object bean, String beanName)
throws BeansException
if ("person".equals(beanName))
System.out
.println("MyInstantiationAwareBeanPostProcessor.postProcessPropertyValues()...");
return pvs;
@#postProcessBeforeInstantiation方法在调用者通过getBean向容器请求某个Bean时,实例化Bean前进行调用,进而调用Bean的构造方法实例化对象;
@#postProcessAfterInstantiation方法在在实例化Bean后调用,对已经实例化的对象进行"梳妆打扮"; @#postProcessPropertyValues方法在postProcessAfterInstantiation方法对Bean进行了处理后调用, 将配置值设置到Bean对应的属性中,进而调用Bean相应的设置方法设置属性;
上图中路上背景标记的是Bean生命接口,这些由Bean自身实现的,看看我们Bean级的生命接口: 自定义Person类,实现了BeanFactoryAware, BeanNameAware,InitializingBean, DisposableBean接口:
/**
* 自定义Person Bean,实现了Bean级生命接口,管理Bean生命周期的接口
* @author Ysjian
*
*/
public class Person implements BeanFactoryAware, BeanNameAware,
InitializingBean, DisposableBean
private int id;
private String name;
private int age;
private Date birthday;
public int getId()
return id;
public void setId(int id)
System.out.println("Person.setId()...");
this.id = id;
public int getAge()
return age;
public void setAge(int age)
System.out.println("Person.setAge()...");
this.age = age;
public String getName()
return name;
public void setName(String name)
System.out.println("Person.setName()...");
this.name = name;
public Date getBirthday()
return birthday;
public void setBirthday(Date birthday)
System.out.println("Person.setBirthday()...");
this.birthday = birthday;
private BeanFactory beanFactory;
private String beanName;
public Person()
System.out.println("constructor is invoked....");
/**
* 1.BeanNameAware接口的方法,
* 在Bean的各项属性值设置完毕后调用,设置该Bean对应的名称
*/
public void setBeanName(String beanName)
System.out.println("Person.setBeanName()....");
this.beanName = beanName;
/**
* 2.BeanFactoryAware的接口方法,
* 在该Bean的名称设置好之后调用,将该Bean的实例设置到容器中
*/
public void setBeanFactory(BeanFactory beanFactory) throws BeansException
System.out.println("Person.setBeanFactory()...");
this.beanFactory = beanFactory;
/**
* 3.InitializingBean的接口方法,
* 在后处理器BeanPostProcessor对Bean加工操作以后调用
*/
public void afterPropertiesSet() throws Exception
System.out.println("Person.afterPropertiesSet()....");
/**
* 4.DisposableBean的接口方法,
* singleton单例Bean在容器销毁后调用的方法
*/
public void destroy() throws Exception
System.out.println("Person.destroy()...");
/**
* 5.通过Bean的init-method指定初始化方法
*/
public void myInit()
System.out.println("Person.myInit()...");
/**
* 6.通过Bean的destroy-method指定初始化方法,
* 单例Bean在DisposableBean#destroy调用后调用,释放资源等操作
*/
public void myDestory()
System.out.println("Person.myDestory()...");
@Override
public String toString()
return "Person [birthday=" + birthday + ", id=" + id + ", name=" + name
+ "]";
@#setBeanName是BeanNameAware接口的方法,在Bean的各项属性值设置完毕后调用,设置该Bean对应的名称;
@#setBeanFactory是BeanFactoryAware的接口方法,在该Bean的名称设置好之后调用,将该Bean的实例设置到容器中;
@#afterPropertiesSet是InitializingBean的接口方法,在后处理器BeanPostProcessor对Bean加工操作以后调用,所以在这个过程之前有一个Spring级的后处理器BeanPostProcessor做了一次加工; @#myInit是自定义的方法,通过Bean的init-method属性指定初始化的方法,进而BeanPostProcessor在一次对Bean进行一次加工; @#destroy是DisposableBean的接口方法,singleton单例Bean在容器销毁后调用的方法; @#myDestory是自定义的方法,单例Bean在DisposableBean#destroy调用后调用,释放资源等操作;
刚才说过在InitializingBean#afterPropertiesSet放法调用之前有一个Spring级的后处理器进行了加工,在init-method方法执行后这个后处理器又进行了一次加工,那么来看看这个关键的后处理器: MyBeanPostProcessor,实现了BeanPostProcessor:
/**
* Spring级的生命周期接口
* @author Ysjian
*
*/
public class MyBeanPostProcessor implements BeanPostProcessor
/**
* 该方法可以对Bean进行特殊处理,为容器提供对Bean进行后续加工处理的切入点,如AOP等
* @param bean 当前的Bean
* @param beanName 当前Bean的配置名
* @return 加工处理后的Bean
*/
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException
if ("person".equals(beanName))
Person person = (Person) bean;
if (person.getAge() > 120)
System.out
.println("MyBeanPostProcessor.postProcessBeforeInitialization()...");
person.setAge(100);
return bean;
/**
* 2.在init-method属性指定的方法执行后对Bean再一次进行加工处理
* @param bean 当前的Bean
* @param beanName 当前Bean的配置名
* @return 加工处理后的Bean
*/
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException
if ("person".equals(beanName))
Person person = (Person) bean;
if (person.getName() == null)
System.out
.println("MyBeanPostProcessor.postProcessAfterInitialization()...");
person.setName("ysjian");
return bean;
@详细说明,代码中注释了;
那么多的代码,有些乱了,再回到那个途中,理清一下思路,就不难理解了,就是两个Spring级的生命周期接口和四个Bean级的生命周期接口的共同配合演绎了一个Bean的出生与死亡。 通常情况下,Spring容器级生命周期接口是解决某些Bean共性化处理的问题的,而Bean级生命周期接口是完成Bean个性化的,也就是我们使用者定制的。 之前说过,Spring级的生命周期接口被称为后处理器,不用Bean自身实现的,如果我们需要手动添加一个很特殊的功能,就要自定义后处理器去实现相应的接口然后覆盖方法,添加自己的业务逻辑,上面就是这么做了, 接下来,我们怎么让这些后处理器装配起来呢?来看一段代码:
public class BeanLifeCycle
public static void main(String[] args)
LifeCycleBeanFactory();
private static void LifeCycleBeanFactory()
Resource resource = new ClassPathResource("beans.xml");
BeanFactory bf = new XmlBeanFactory(resource);
ConfigurableBeanFactory cFactory = (ConfigurableBeanFactory) bf;
// 向容器注册MyBeanPostProcessor
cFactory.addBeanPostProcessor(new MyBeanPostProcessor());
// 向容器注册MyInstantiationAwareBeanPostProcessor
cFactory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor());
Person person1 = bf.getBean("person", Person.class);
// 如果bean的scope="singleton",Spring容器继续负责bean的生命周周期,放入缓存池中,为true,否则否则交给用户,结果为false,
person1.setName("first ysjian");
Person person2 = bf.getBean("person", Person.class);
System.out.println("person1 == person2:" + (person1 == person2));
// 关闭容器
((XmlBeanFactory) bf).destroySingletons();
@ConfigurableBeanFactory#addBeanPostProcessor是用来注册我们自定义的后处理器的,这样注册后,才生效。
@来看看我们的Bean的生命演化结果:
MyInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation()...
constructor is invoked....
MyInstantiationAwareBeanPostProcessor.postProcessAfterInstantiation()...
MyInstantiationAwareBeanPostProcessor.postProcessPropertyValues()...
Person.setAge()...
Person.setBirthday()...
Person.setId()...
Person.setBeanName()....
Person.setBeanFactory()...
MyBeanPostProcessor.postProcessBeforeInitialization()...
Person.setAge()...
Person.afterPropertiesSet()....
Person.myInit()...
MyBeanPostProcessor.postProcessAfterInitialization()...
Person.setName()...
Person.setName()...
person1 == person2:true
Person.destroy()...
Person.myDestory()...
思考:那么,清楚了Bean在BeanFactory中的一个生命流程之后,我们是不是可以更灵活的去定制我们需要的功能,如在init-method,destroy-method,注册多个后处理器呢???
当然一般的应用,我们不需要关注Spring容器级别的接口,因为Spring强大的功能大部分能满足我们的开发了。但要补上一句:
在后面要介绍的Spring的AOP功能的实现也关系到了BeanPostProcessor这个接口,所以继续学习中~ ~
3.ApplicationContext中Bean的初始化经历了一系列过程: 大概了解了BeanFactory中Bean的生命周期,在ApplicationContext中有一些小的变化,前面介绍过,BeanFactory是面向Spring容器的,ApplicationContext是面向开发者的 再介绍一张图:
粗略看,与前面的图区别不大,那么为什么列出这个图呢,仔细看看,多了一个ApplicationContextAware接口,增加了一个 ApplicationContextAware#setApplicationContext处理 这里也要介绍一下一个工厂后处理器:BeanFactoryPostProcessor,看一段代码:
/**
* 自定义工厂后处理器
* @author Ysjian
*
*/
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor
/**
* 在getBean之前对配置文件中配置的Bean的信息进行个性化更改
*/
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf)
throws BeansException
BeanDefinition bd = bf.getBeanDefinition("car");
//更改配置文件的配置信息
bd.getPropertyValues().addPropertyValue("brand", "BMW");
System.out
.println("MyBeanFactoryPostProcessor.postProcessBeanFactory()");
@这个自定义的工厂后处理器就是在getBean之前对配置文件的信息进行个性化更改
那么如何让它工作呢?这就需要在beanm.xml中进行配置了:
beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="person" class="com.meritit.ysjian.spring3learning.beanlife.Person"
init-method="myInit" destroy-method="myDestory" scope="singleton"
p:id="1001" p:age="125" p:birthday="2013/03/05" />
<bean id="myBeanPostProcessor"
class="com.meritit.ysjian.spring3learning.beanfactory.MyBeanPostProcessor" />
<bean id="myBeanFactoryPostProcessor"
class="com.meritit.ysjian.spring3learning.context.MyBeanFactoryPostProcessor" />
<bean id="myInstantiationAwareBeanPostProcessor"
class="com.meritit.ysjian.spring3learning.beanfactory.MyInstantiationAwareBeanPostProcessor" />
</beans>
@ApplicationContext只需要在配置文件中定义自定义的后处理器,让后容器启动的时候回自动识别,让后自动注册;
在Java中的小测试:
private static void LifeCycleApplicationContext()
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
Person person1 = ac.getBean("person", Person.class);
// 如果bean的scope="singleton",Spring容器继续负责bean的生命周周期,放入缓存池中,为true,否则否则交给用户,结果为false,
// person1.setName("first ysjian");
Person person2 = ac.getBean("person", Person.class);
System.out.println("person1 == person2:" + (person1 == person2));
System.out.println(person1.getName()+"--------------");
@结果是:ysjian003--------------,就是说我们自定义的工厂后处理器在getBean调用之前将配置文件中没有的name属性设置为ysjian003了,但不常用。
小结:多次强调了ApplicationContext是面向开发者的,在前面介绍中,ApplicationContext使用起来比BeanFactory要方便快捷,所以在开发中推荐使用ApplicationContext。 哈,告一段落,祝各位 程序猿 以及 程序媛 还有伟大的 攻城狮 学习和工作愉快~
开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系
以上是关于Bean Lifecycle in Spring的主要内容,如果未能解决你的问题,请参考以下文章
Spring的Lifecycle和SmartLifecycle,可以没用过,但不能不知道!