一、自定義bean的生命周期
通過實(shí)現(xiàn)spring的InitializingBean和DisposableBean接口,可以讓容器來管理bean的生命周期。容器在調(diào)用afterPropertiesSet()方法后和調(diào)用destroy()方法前會允許bean在初始化和銷毀bean時執(zhí)行以下操作,但使用這些接口就與springAPI產(chǎn)生了耦合。
解耦合的處理方式有兩種:
使用JSR-250的@PostConstruct和@PreDestroy注解是spring應(yīng)用生命周期回調(diào)的最佳實(shí)踐。
使用init-method和destroy-method的定義來解耦spring接口。
spring框架使用BeanPostProcessor接口的實(shí)現(xiàn)來處理接口的回調(diào),BeanPostProcessor能找到并調(diào)用合適的方法。如果需要定制spring不直接提供的生命周期行為,可自行實(shí)現(xiàn)一個BeanPostProcessor。
除初始化回調(diào)和銷毀回調(diào)外,spring管理的對象也實(shí)現(xiàn)了Lifecycle接口,讓管理的對象在容器的生命周期內(nèi)啟動或關(guān)閉。
1、初始化回調(diào)
org.springframework.beans.factory.InitializingBean接口允許bean在所有必要依賴配置完成后執(zhí)行初始化bean。

不建議使用InitializingBean接口,否則會將代碼耦合到spring特定接口上。使用@PostConstruct注解或指定一個POJO的實(shí)現(xiàn)方法的方式更好。在基于XML的配置元數(shù)據(jù)時可以使用init-method屬性來指定一個沒有參數(shù)的方法。使用Java配置時也可以使用@Bean中的init-method屬性初始化回調(diào)。示例如下:
<bean id="initBean" class="xx.InitBean" init-method="init" />
public class?InitBean {
? ? public void init(){...}
}
以下方式等效于以上方式,但會耦合到spring中:
<bean id="initBean" class="xx.InitBean" />
public class?InitBean implements InitializingBean?{
????public void afterPropertiesSet(){...}
}
2、銷毀回調(diào)
實(shí)現(xiàn)org.springframework.beans.factory.DisposableBean接口就可以讓容器通過回調(diào)來銷毀bean鎖引用的資源。

與InitializingBean接口類似,同樣不推薦使用。使用@PreDestroy注解或指定一個bean支持的配置方法或在基于XML的配置元數(shù)據(jù)中,在bean標(biāo)簽上指定destroy-method屬性。在基于Java的配置中可以配置@Bean中的destroy-method屬性實(shí)現(xiàn)銷毀回調(diào)。示例如下:
<bean id="desBean" class="xx.DesBean" destroy-method="cleanup" />
public class?DesBean {
????public void cleanup(){...}
}
以下方式等效于以上方式,但會耦合到spring中:
<bean id="desBean" class="xx.DesBean" />
public class?DesBean?implements?DisposableBean?{
????public void?destroy(){...}
}
3、結(jié)合生命周期機(jī)制
spring2.5之后,有3種方式來控制bean的生命周期行為:
InitializingBean和DisposableBean回調(diào)接口
自定義的init()和destroy()方法
使用@PostConstruct和@PreDestroy注解
如果一個bean配置了多個生命周期機(jī)制,并且含有不同的方法名,執(zhí)行順序如下:
初始化時:
1)包含@PostConstruct注解的方法
2)在InitializingBean接口中的afterPropertiesSet()方法
3)自定義的init()方法
銷毀時:
1)包含@PreDestroy注解的方法
2)在DisposableBean接口中的destroy()方法
3)自定義的destroy()方法
4、啟動和關(guān)閉回調(diào)
Lifecycle接口中為任何有生命周期需求的對象定義了一些基本方法,spring管理的任何對象都可以實(shí)現(xiàn)其接口。當(dāng)ApplicationContext接收到了啟動或停止信號時,它會通知所有上下文包含的生命周期對象,通過LifecycleProcessor接口來串聯(lián)上下文中的Lifecycle來實(shí)現(xiàn)對象。


LifecycleProcessor是對Lifecycle的擴(kuò)展,它增加了2個方法來對上下文的刷新和關(guān)閉作出反應(yīng)。
如果不同bean之間存在depends-on的關(guān)系,被依賴的一方需要更早的啟動或關(guān)閉,有時直接的依賴是未知的,開發(fā)者可能并不值得哪些類型需要更早的進(jìn)行初始化。SmartLifecycle接口定義了另外一種選項(xiàng),即其父接口Phased中的getPhase()方法。


當(dāng)啟動時,擁有最低phased的對象優(yōu)先啟動,當(dāng)關(guān)閉時,按相反的順序。如果一個對象實(shí)現(xiàn)了SmartLifecycle接口,其getPhase()返回了Integer.MIN_VALUE,就會讓該對象最早啟動,最晚銷毀。如果getPhase()返回了Integer.MAX_VALUE,就會讓該對象最晚啟動,最早銷毀。當(dāng)使用phased的值時,需要知道正常沒有實(shí)現(xiàn)SmartLifecycle的Lifecycle對象的默認(rèn)值,此值為0。任何負(fù)值都會將標(biāo)明對象在標(biāo)準(zhǔn)組件啟動前啟動,在標(biāo)準(zhǔn)組件銷毀后銷毀。
SmartLifecycle定義了一個stop()回調(diào)函數(shù),任何實(shí)現(xiàn)了其接口的方法都必須在關(guān)閉流程完成后調(diào)用回調(diào)中的run()方法,這樣可以使其異步關(guān)閉。LifecycleProcessor的默認(rèn)實(shí)現(xiàn)DefaultLifecycleProcessor會等到配置的時間超時后再調(diào)用回調(diào),每階段的超時默認(rèn)30S??梢酝ㄟ^定義一個名為lifecycleProcessor的bean來覆蓋默認(rèn)的生命周期處理器,若需要配置超時時間,可如下配置:
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
? ? <!--?超時時間,單位毫秒 -->
? ? <property name="timeoutPerShutdownPhase" value="10000" />
</bean>
SmartLifecycle接口定義回調(diào)方法來刷新和關(guān)閉上下文,如果關(guān)閉則說明stop()已被調(diào)用,就會驅(qū)動關(guān)閉流程;如果上下文正在關(guān)閉,就不會發(fā)生這種情況。刷新的回調(diào)會使用SmartLifecycle的另一特性:當(dāng)上下文刷新完畢就會調(diào)用回調(diào),默認(rèn)的生命周期處理器會檢查每一個SmartLifecycle對象的isAutoStartup()返回值。若為true,對象將會自動啟動而非等待明確的上下文調(diào)用,或調(diào)用自己的start()。phased的值及depends-on會決定對象啟動和銷毀的順序。
5、在非web應(yīng)用中關(guān)閉Spring IoC容器
在桌面客戶端等非web環(huán)境下使用Spring IoC容器,需要在JVM上注冊一個關(guān)閉的鉤子,確保在關(guān)閉IoC容器時能夠調(diào)用相關(guān)的銷毀方法來釋放引用的資源,同時也需要正確的配置和實(shí)現(xiàn)銷毀回調(diào)。
在ConfigurableApplicationContext接口調(diào)用registerShutdownHook()方法來注冊銷毀的鉤子。示例如下:
ConfigurableApplicationContext ctx = new?ClassPathXmlApplicationContext("spring.xml");
...
ctx.registerShutdownHook();
6、ApplicationContextAware和BeanNameAware
當(dāng)ApplicationContext在創(chuàng)建實(shí)現(xiàn)了org.springframework.context.ApplicationContextAware的對象時,該對象的實(shí)例會包含一個到ApplicationContext的引用。這樣bean可以通過編程的方式操作和創(chuàng)建ApplicationContext。

通過ApplicationContext接口或通過將引用轉(zhuǎn)換為已知接口的子類,如ConfigurableApplicationContext,其中一個用法是可以通過編程的方式來獲取其他bean。但不建議這樣做,這樣代碼會耦合到spring。ApplicationContext的其他方法可以提供資源訪問、發(fā)布應(yīng)用事件、進(jìn)入MessageSource等功能。
自動裝載也是獲得ApplicationContext的一種方式,構(gòu)造函數(shù)和通用類型的裝載方式,是指可以通過構(gòu)造函數(shù)或setter方法注入,也可以通過注解注入的方式。
當(dāng)ApplicationContext創(chuàng)建一個實(shí)現(xiàn)了org.springframework.beans.factory.BeanNameAware接口的類,這個類就可以針對其名稱進(jìn)行配置。這個回調(diào)的調(diào)用發(fā)生在屬性配置完成后,初始化回調(diào)之前,如InitializingBean.afterPropertiesSet()及自定義的初始化方法等。

7、其他Aware接口
spring還提供了其他Aware接口來讓bean通知容器,這些bean需要一些具體的注入的依賴信息,如下:
ApplicationContextAware:聲明的ApplicationContext
ApplicationEventPublisherAware:ApplicationContext中的事件發(fā)布器
BeanClassLoaderAware:加載bean使用的類加載器
BeanFactoryAware:聲明的BeanFactory
BeanNameAware:bean的名稱
LoadTimeWeaverAware:加載期間處理類定義的Weaver
MessageSourceAware:解析消息的配置策略
NotificationPublisherAware:spring JMX通知發(fā)布器
ResourceLoaderAware:配置的資源加載器
上面這些接口的不建議使用,因?yàn)槠鋾`反IOC原則。
二、定義繼承
bean的定義可以包含構(gòu)造方法參數(shù)、屬性值、容器特定的信息等。子bean定義可以從父bean定義的配置元數(shù)據(jù)來繼承,它可以覆蓋或添加一些所需的值。父子bean是一種典型的模板形式。
如果編程式的使用ApplicationContext接口,子bean的定義可以通過ChildBeanDefinition類來表示,但使用更多的是在類似于ClassPathXmlApplicationContext中聲明式的配置bean的定義。如果使用基于XML的配置時,可以在子bean中使用parent屬性,用于標(biāo)識父bean。
<bean id="testBean" abstract="true" class="org.springframework.beans.TestBean">
? <property name="name" value="parent"/>
? <property name="age" value="10"/>
</bean>
<bean id="testBean1" class="org.springframework.beans.TestBean1" parent="testBean" init-method="initialize">
? <property name="name" value="override"/>
</bean>
子bean如果沒有指定class,它將使用父bean定義的class或進(jìn)行重載。在重載情況下子bean必須與父bean兼容,即它必須接受父bean的屬性值。
子bean定義可以從父bean繼承作用域、構(gòu)造器參數(shù)、屬性值和可重寫的方法,還可以增加新值。開發(fā)者指定的任何作用域、初始化方法、銷毀方法、靜態(tài)工廠方法設(shè)置,都會覆蓋相應(yīng)的父bean設(shè)置。其余的設(shè)置總是取自子bean的定義,如depends-on、autowire mode、dependency check、singleton、scope和lazy init等。
上例中使用abstract屬性明確表明父bean是抽象的,如果父bean定義沒有明確指出所屬類,則需要標(biāo)記父bean定義為abstract。
<bean id="testBean" abstract="true">
? <property name="name" value="parent"/>
? <property name="age" value="10"/>
</bean>
<bean id="testBean1" class="org.springframework.beans.TestBean1" parent="testBean" init-method="initialize">
? <property name="name" value="override"/>
</bean>
上例中只有一個bean定義為abstract,它只能作為一個純粹的為子bean定義的bean模板。獨(dú)立使用這樣一個abstract的父bean,把它作為另一個bean的引用,或根據(jù)這個父bean的id顯式調(diào)用getBean(),都將返回錯誤。
ApplicationContext默認(rèn)會預(yù)實(shí)例化所有單例bean,如果把一個bean定義僅作為模板來用,同時給它指定了class屬性,就必須設(shè)置abstract=true,否則ApplicationContext就會預(yù)實(shí)例化這個abstract?bean。
--參考文獻(xiàn)《Srping5開發(fā)大全》