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。
哈,告一段落,祝各位 程序猿 以及 程序媛 還有偉大的 攻城獅 學習和工作愉快~















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章