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 IOC DI LifeCycle

Spring的Lifecycle和SmartLifecycle,可以没用过,但不能不知道!

Spring的Lifecycle和SmartLifecycle,可以没用过,但不能不知道!

Spring Bean生命周期回调

Spring Bean生命周期回调方法

Spring 自定义 bean 的生命周期