spring生命週期

http://sexycoding.iteye.com/blog/1046775

開篇先用一張老圖描述下Spring中Bean容器的生命週期。


插敘一下,記得某個博文中提到:“Spring的Bean容器只管理非單例Bean的生命週期,單例Bean的生命週期不在管理範圍內”,其實我認爲這句話恰好說反了。首先明確一點,並非Spring容器中所有的Bean都有生命週期行爲,只有接受容器管理生命週期的Bean才具有生命週期行爲:而單例(Singleton)Bean接受容器管理,非單例(non-singleton)Bean在實例化後,完全交給了客戶端代碼管理,容器不再跟蹤其生命週期,每次客戶請求,容器都會創建一個新的實例,所以Spring容易無法知曉Bean何時銷燬。

繼續剛纔的話題——Bean容器的生命週期。其實上圖有個節點沒有畫出,就是在實例化所有Bean之前會執行BeanFactoryPostProcessors。不過也不care,因爲這和Bean的生命週期沒有太大關係,所以沒有提及也屬正常,權且忽略該節點。
從圖中,我們可以看到實例化Bean的過程中有以下幾個節點:
1)設置屬性值;
2)調用Bean中的BeanNameAware.setBeanName()方法,如果該Bean實現了BeanNameAware接口;
3)調用Bean中的BeanFactoryAware.setBeanFactory()方法,如果該Bean實現了BeanFactoryAware接口;
4)調用BeanPostProcessors.postProcessBeforeInitialization()方法;
5)調用Bean中的afterPropertiesSet方法,如果該Bean實現了InitializingBean接口;
6)調用Bean中的init-method,通常是在配置bean的時候指定了init-method,例如:<bean class="beanClass" init-method="init"></bean>
7)調用BeanPostProcessors.postProcessAfterInitialization()方法;
8)如果該Bean是單例的,則當容器銷燬並且該Bean實現了DisposableBean接口的時候,調用destory方法;如果該Bean是prototype,則將準備好的Bean提交給調用者,後續不再管理該Bean的生命週期。

好了,簡單了描述了下那幅圖。一切都還太抽象了,作爲程序員,代碼還是最直接的表達方式。那我們就一起看段演示代碼吧。

首先,爲達到演示效果,我們準備兩個待測試的Bean,代碼如下:
Java代碼
@Component
public class DemoBean implements BeanFactoryAware, BeanNameAware,
InitializingBean, DisposableBean {
@PostConstruct
public void init() {
System.out.println("DemoBean: init-method");
}
public void destroy() throws Exception {
System.out.println("DemoBean: destroy-method!");
}
public void afterPropertiesSet() throws Exception {
System.out.println("DemoBean: after properties set!");
}
public void setBeanName(String name) {
System.out.println("DemoBean: beanName aware, [name=" + name + "]");
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("DemoBean: beanFactory aware, [beanFactory=" + beanFactory.toString() + "]");
}
}


Java代碼
public class AnotherDemoBean implements InitializingBean {

@PostConstruct
public void postConstruct() {
System.out.println("AnotherDemoBean: postConstruct-method");
}
public void init() {
System.out.println("AnotherDemoBean: init-method");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("AnotherDemoBean: after properties set!");
}
}

上面兩個Bean大致相同,區別在於第一個Bean使用註解方式注入,第二個Bean我們使用配置文件方式,並指定其init-method,用於觀察init-method與postConstruct的執行先後。

我們這個演示Bean實現了BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean這幾個接口,其實這些接口也可理解爲Spring容器的一個個擴展點。

然後,我們再編寫一個BeanPostProcessor,用於演示生命週期中的步驟4和步驟7。 代碼如下:
Java代碼
@Component
public class DemoBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("DemoBeanPostProcessor: post process before initialization, [beanName=" + beanName + ", bean=" + bean + "]");
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("DemoBeanPostProcessor: post process before initialization, [beanName=" + beanName + ", bean=" + bean + "]");
return bean;
}
}

最後,我們編寫測試類,以及Spring的配置文件,這裏我們使用ClassPathXMLApplicationContext加載配置文件和初始化Spring容器。一起看下配置文件和測試類代碼:
applicationContext.xml:
Xml代碼
<?xml version="1.0" encoding="GBK"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:component-scan base-package="com.shansun.multidemo"></context:component-scan>
<bean class="com.shansun.multidemo.spring.lifecycle.AnotherDemoBean" init-method="init"></bean>
</beans>

Main.java
Java代碼
public class Main {
@SuppressWarnings("unused")
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
}

好了,一切就緒,我們就靜觀程序輸出吧:
Java代碼
DemoBean: beanName aware, [name=demoBean]
DemoBean: beanFactory aware, [beanFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory@888e6c:defining beans [demoBean,demoBeanFactoryPostProcessor,demoBeanPostProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,com.shansun.multidemo.spring.lifecycle.AnotherDemoBean#0]; root of factory hierarchy]
DemoBean: init-method
DemoBeanPostProcessor: post process before initialization, [beanName=demoBean, bean=com.shansun.multidemo.spring.lifecycle.DemoBean@1deeb40]
DemoBean: after properties set!
DemoBeanPostProcessor: post process before initialization, [beanName=demoBean, bean=com.shansun.multidemo.spring.lifecycle.DemoBean@1deeb40]
AnotherDemoBean: postConstruct-method
DemoBeanPostProcessor: post process before initialization, [beanName=com.shansun.multidemo.spring.lifecycle.AnotherDemoBean#0, bean=com.shansun.multidemo.spring.lifecycle.AnotherDemoBean@1a7ddcf]
AnotherDemoBean: after properties set!
AnotherDemoBean: init-method
DemoBeanPostProcessor: post process before initialization, [beanName=com.shansun.multidemo.spring.lifecycle.AnotherDemoBean#0, bean=com.shansun.multidemo.spring.lifecycle.AnotherDemoBean@1a7ddcf]

和我們預期的是否一樣呢?是的。觀察結果發現一個有趣的地方:在配置文件中指定的init-method和使用@PostConstruct註解的方法,孰先孰後呢,兩者是否等同呢?後續我將通過分析源碼給出結論。

我們通過演示代碼也驗證了Bean容器的生命週期,但是還缺點什麼吧。對了,透過Spring源碼講述Bean容器的生命週期是否更加直觀和令人信服呢?下面我們去Spring源碼中一探究竟。這裏我們選用的是spring-2.5.6.SEC02。

大家應該都知道Spring中BeanFactory和ApplicationContext的關係了吧,ApplicationContext繼承自BeanFactory,所以可以操作到bean。更詳細的內容可以參考許令波同學的《Spring框架的設計理念與設計模式分析》,裏面有較清晰的分析。

好了,閒話不多說。

首先,我們探視下實例化Bean的方法initializeBean,該方法在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory類下,一起看下該段代碼:
Java代碼
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}

if (bean instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
}

if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(this);
}

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}

if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}

這樣夠直觀了吧,是不是和前文描述的一樣呢,J

本文源代碼下載:https://lb-multi-demo.googlecode.com/svn/trunk/spring-lifecycle-test

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