Spring生命周期回調(diào)與容器擴(kuò)展詳解
本篇主要總結(jié)下Spring容器在初始化實(shí)例前后,提供的一些回調(diào)方法和可擴(kuò)展點(diǎn)。利用這些方法和擴(kuò)展點(diǎn),可以實(shí)現(xiàn)在Spring初始化實(shí)例前后做一些特殊邏輯處理。
下面主要介紹:
類級(jí)別的生命周期初始化回調(diào)方法init-method配置、InitializingBean接口和PostConstruct注解
容器級(jí)別的擴(kuò)展BeanPostProcessor接口和BeanFactoryPostProcessor接口
1.類級(jí)別生命周期回調(diào)
1.1init-method
init-method是在Spring配置文件中聲明bean的時(shí)候的一個(gè)配置項(xiàng)。init-method配置項(xiàng)的值為類中的一個(gè)無參方法,但可拋出異常。該方法會(huì)在Spring容器實(shí)例化對(duì)象并設(shè)置完屬性值之后被調(diào)用。
init-method能實(shí)現(xiàn)的功能與InitializingBean接口、PostConstruct注解一致
Spring配置文件及測試類如下:
<bean id = "initMethodBeanService" class="name.liuxi.spring.ext.InitMethodBeanService" init-method="init"> <property name="f2" value="2"/> </bean>
測試類如下:
public class InitMethodBeanService {
private static Integer f1;
private Integer f2;
static {
f1 = 1;
System.out.println("InitMethodBeanService static block execute...");
}
public InitMethodBeanService(){
System.out.println("InitMethodBeanService construct method execute...");
}
public void init(){
System.out.println("InitMethodBeanService init method execute...");
}
public Integer getF2() {
return f2;
}
public void setF2(Integer f2) {
this.f2 = f2;
System.out.println("InitMethodBeanService setF2 method execute...");
}
}
執(zhí)行結(jié)果打印如下:
InitMethodBeanService static block execute... InitMethodBeanService construct method execute... InitMethodBeanService setF2 method execute... InitMethodBeanService init method execute... test method execute...
1.2InitializingBean接口
參照:Spring官方文檔beans-factory-lifecycle-initializingbean
InitializingBean接口中聲明了一個(gè)方法afterPropertiesSet,該方法會(huì)在Spring容器實(shí)例化對(duì)象并設(shè)置完屬性值之后被調(diào)用。和上面的init-method實(shí)現(xiàn)的功能一致,因此Spring不推薦使用InitializingBean接口。
例子比較簡單,不列出來了
1.3PostConstruct注解
翻譯:Spring官方文檔beans-postconstruct-and-predestroy-annotations
@PostConstruct注解是和init-method、InitializingBean接口實(shí)現(xiàn)效果一致的生命周期回調(diào)方法
@PostConstruct
public void postConstruct(){
System.out.println("PostConstructService postConstruct method execute...");
}
總結(jié)下上面三個(gè)生命周期回調(diào)方法init-method、InitializingBean接口、@PostConstruct注解
1.都是針對(duì)單個(gè)類的實(shí)例化后處理
2.執(zhí)行時(shí)間都是在類實(shí)例化完成,且成員變量完成注入之后調(diào)用的
3.對(duì)于init-method,還可以在Spring配置文件的beans元素下配置默認(rèn)初始化方法,配置項(xiàng)為default-init-method
4.若以上三種方式配置的初始化方法都不一樣,則執(zhí)行順序?yàn)椋篅PostConstruct注解方法–>InitializingBean的afterPropertiesSet–>init-method方法;若三種方式配置的方法一樣,則方法只執(zhí)行一次(參照:Spring官方文檔beans-factory-lifecycle-combined-effect)
5.有初始化回調(diào)方法,對(duì)應(yīng)的也有銷毀的回調(diào)方法。@PostConstruct注解方法–>InitializingBean的afterPropertiesSet–>init-method方法分別對(duì)應(yīng)@PreDestroy注解方法–>DisposableBean的destroy–>destroy-method方法
2.容器級(jí)別擴(kuò)展
翻譯:Spring官方文檔3.8ContainerExtensionPoints
通常情況下,開發(fā)人員無需自定義實(shí)現(xiàn)一個(gè)ApplicationContext的子類去擴(kuò)展SpringIOC容器,SpringIOC容器通過對(duì)外暴露的一些接口,可實(shí)現(xiàn)對(duì)SpringIOC容器的擴(kuò)展。
2.1BeanPostProcessor接口
2.1.1bean實(shí)例初始化后處理器及后處理器鏈
BeanPostProcessor接口定義了兩個(gè)容器級(jí)別的回調(diào)方法postProcessBeforeInitialization和postProcessAfterInitialization,用于在初始化實(shí)例后的一些邏輯處理,會(huì)針對(duì)容器中的所有實(shí)例進(jìn)行處理。實(shí)現(xiàn)了BeanPostProcessor接口的類,稱之為bean實(shí)例初始化后處理器。
若在SpringIOC容器中集成了多個(gè)實(shí)例初始化后處理器,這些后處理器構(gòu)成的集合稱之為bean實(shí)例初始化后處理器鏈。
postProcessBeforeInitialization方法在類實(shí)例化且成員變量注入完成之后執(zhí)行,初始化方法(例如InitializingBean的afterPropertiesSet方法)之前執(zhí)行
postProcessAfterInitialization方法在類實(shí)例化且成員變量注入完成之后執(zhí)行,初始化方法(例如InitializingBean的afterPropertiesSet方法)之后執(zhí)行
總結(jié):
1.實(shí)例初始化后處理器多用于對(duì)實(shí)例的一些代理操作。Spring中一些使用到AOP的特性也是通過后處理器的方式實(shí)現(xiàn)的。
2.實(shí)例初始化后處理器鏈?zhǔn)嵌鄠€(gè)后處理器,就會(huì)有執(zhí)行順序的問題,可以通過實(shí)現(xiàn)Ordered接口,指定后處理的執(zhí)行順序,Ordered接口聲明了getOrder方法,方法返回值越小,后處理的優(yōu)先級(jí)越高,越早執(zhí)行。
3.在通過實(shí)現(xiàn)BeanPostProcessor接口自定義實(shí)例初始化后處理器的時(shí)候,建議也實(shí)現(xiàn)Ordered接口,指定優(yōu)先級(jí)。
4.這些后處理器的作用域是當(dāng)前的SpringIOC容器,即后處理器被聲明的SpringIOC容器。對(duì)于有層次結(jié)構(gòu)的SpringIOC容器,實(shí)例初始化后處理器鏈不會(huì)作用于其他容器所初始化的實(shí)例上,即使兩個(gè)容器在同一層次結(jié)構(gòu)上。
5.實(shí)例初始化后處理器的實(shí)現(xiàn)類只需要和普通的被Spring管理的bean一樣聲明,SpringIOC容器就會(huì)自動(dòng)檢測到,并添加到實(shí)例初始化后處理器鏈中。
6.相對(duì)于自動(dòng)檢測,我們也可以調(diào)用ConfigurableBeanFactory的addBeanPostProcessor方法,以編程的方式將一個(gè)實(shí)例初始化后處理器添加到實(shí)例初始化后處理器鏈中。這在需要判定添加條件的場景下比較實(shí)用。這種編程式的方式會(huì)忽略到實(shí)現(xiàn)的Ordered接口所指定的順序,而會(huì)作用于所有的被自動(dòng)檢測的實(shí)例初始化后處理器之前。
2.1.2bean實(shí)例初始化后處理器與AOP
BeanPostProcessor是一個(gè)特殊的接口,實(shí)現(xiàn)這個(gè)接口的類會(huì)被作為Spring管理的bean的實(shí)例的后處理器。因此,在Spring應(yīng)用上下文啟動(dòng)的一個(gè)特殊階段,會(huì)直接初始化所有實(shí)現(xiàn)了BeanPostProcessor接口的實(shí)例,以及該實(shí)例所引用的類也會(huì)被實(shí)例化。然后作為后處理器應(yīng)用于其他普通實(shí)例。
由于AOP的自動(dòng)代理是以實(shí)例化后處理器的方式實(shí)現(xiàn)的,所以無論是bean實(shí)例初始化后處理器鏈實(shí)例還是其引用的實(shí)例,都不能被自動(dòng)代理。因而,不要在這些實(shí)例上進(jìn)行切面織入。(對(duì)于這些實(shí)例,會(huì)產(chǎn)生這樣的日志消息:“類foo不能被所有的實(shí)例化后處理器鏈處理,即不能被自動(dòng)代理”)。
注意:當(dāng)實(shí)例化后處理器以autowiring或@Resource的方式引用其他bean,Spring容器在以類型匹配依賴注入的時(shí)候,可能會(huì)注入非指定的bean(例如:實(shí)例化后處理器實(shí)現(xiàn)類以Resource方式依賴bean,若set方法和被依賴的bean的名稱一致或者被依賴bean未聲明名稱,則依賴注入會(huì)以類型匹配的方式注入,此時(shí)可能會(huì)注入非指定的bean)。這也會(huì)導(dǎo)致自動(dòng)代理或其他方式的實(shí)例化后處理器處理失敗。
2.1.3bean實(shí)例初始化后處理器示例
public class BeanPostProcessorService implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println("BeanPostProcessorService postProcessAfterInitialization method execute... ");
return o;
}
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("BeanPostProcessorService postProcessBeforeInitialization method execute... ");
return o;
}
}
2.2BeanFactoryPostProcessor接口
2.2.1beanfactory后處理器
通過實(shí)現(xiàn)BeanFactoryPostProcessor接口,可以讀取容器所管理的bean的配置元數(shù)據(jù),在bean完成實(shí)例化之前進(jìn)行更改,這些bean稱之為beanfactory后處理器。
BeanFactoryPostProcessors與BeanPostProcessor接口的異同點(diǎn):
相同點(diǎn):
都是容器級(jí)別的后處理器
都可配置多個(gè)后處理器,并通過實(shí)現(xiàn)Ordered接口,指定執(zhí)行順序
都是針對(duì)接口聲明的容器中所管理的bean進(jìn)行處理,在有層級(jí)結(jié)構(gòu)的容器中,不能處理其他容器中的bean,即使兩個(gè)容器是同一層次
都是只需要在容器中和普通bean一樣聲明,容器會(huì)自動(dòng)檢測到,并注冊(cè)為后處理器
會(huì)忽略延遲初始化屬性配置
不同點(diǎn):
BeanFactoryPostProcessors接口在bean**實(shí)例化前處理bean的配置元數(shù)據(jù),BeanPostProcessor接口在bean實(shí)例化后處理bean的實(shí)例**
BeanFactoryPostProcessors接口也能通過BeanFactory.getBean()方法獲取bean的實(shí)例,這樣會(huì)引起bean的實(shí)例化。由于BeanFactoryPostProcessors后處理器是在所有bean實(shí)例化之前執(zhí)行,通過BeanFactory.getBean()方法會(huì)導(dǎo)致提前實(shí)例化bean,從而打破容器標(biāo)準(zhǔn)的生命周期,這樣可能會(huì)引起一些負(fù)面的影響(例如:提前實(shí)例化的bean會(huì)忽略bean實(shí)例化后處理器的處理)。
2.2.2Spring內(nèi)置及自定義beanfactory后處理器
Spring內(nèi)置了一些beanfactory后處理器(例如:PropertyPlaceholderConfigurer和PropertyOverrideConfigurer)。同時(shí)也支持實(shí)現(xiàn)BeanFactoryPostProcessor接口,自定義beanfactory后處理器。下面說說Spring內(nèi)置的兩個(gè)后處理器和自定義后處理器。
PropertyPlaceholderConfigurer
Spring為了避免主要的XML定義文件的修改而引起的風(fēng)險(xiǎn),提供了配置分離,可以將一些可能變更的變量配置到屬性配置文件中,并在XML定義文件中以占位符的方式引用。這樣,修改配置只需要修改屬性配置文件即可。PropertyPlaceholderConfigurer用于檢測占位符,并替換占位符為配置屬性值。示例如下:
PropertyPlaceholderConfigurer通過jdbc.properties屬性配置文件,在運(yùn)行時(shí),將dataSource這個(gè)bean中數(shù)據(jù)庫相關(guān)信息的屬性占位符替換成對(duì)應(yīng)的配置值。
XML配置如下:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/foo/jdbc.properties"/>
</bean>
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
屬性配置文件jdbc.properties如下:
jdbc.driverClassName=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb:hsql://production:9002 jdbc.username=sa jdbc.password=root
PropertyPlaceholderConfigurer不僅支持屬性配置文件的讀取,也支持讀取系統(tǒng)屬性。通過systemPropertiesMode屬性值可配置讀取優(yōu)先級(jí)。各種取值說明如下:
0:不讀取系統(tǒng)屬性
1:若引用的屬性配置文件中未檢索到對(duì)應(yīng)占位符的配置,則讀取系統(tǒng)屬性。默認(rèn)為1
2:先讀取系統(tǒng)屬性,再讀取引用的屬性配置文件。這種配置可能導(dǎo)致系統(tǒng)屬性覆蓋配置文件。
PropertyOverrideConfigurer
PropertyOverrideConfigurer類可以通過引用屬性配置文件,直接給容器中的bean賦值。當(dāng)一個(gè)bean的屬性被多個(gè)PropertyOverrideConfigurer類實(shí)例賦值時(shí),最后一個(gè)的值會(huì)覆蓋前面的。
還是以上面給上面的dataSource的bean賦值為例:
PropertyOverrideConfigurer類對(duì)屬性配置文件的引用使用一個(gè)新的方式,如下:
<context:property-override location="classpath:override.properties"/>
override.properties屬性配置文件的屬性的命名規(guī)則和上面不同(上面例子中需要保證屬性名和占位符一致),命名規(guī)則是beanName.property
dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql:mydb dataSource.username=sa dataSource.password=root
支持復(fù)合屬性的賦值,但是要保證引用被賦值屬性的對(duì)象非空
例如:foo.fred.bob.sammy=123
自定義bean factory后處理器
自定義bean factory后處理器就是實(shí)現(xiàn)BeanFactoryPostProcessor接口,完成對(duì)Spring容器管理的bean的配置元數(shù)據(jù)進(jìn)行修改。例如:修改類屬性注入的值,示例如下:
定義一個(gè)用戶類UserBean
public class UserBean {
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
Spring XML配置文件配置用戶類,并給用戶名屬性u(píng)serName注入值haha
<bean class="name.liuxi.spring.ext.BeanFactoryPostProcessorService"/> <bean id="user" class="name.liuxi.spring.ext.UserBean"> <property name="userName" value="haha"/> </bean>
下面是自定義的bean factory后處理器,修改屬性u(píng)serName的值為heihei
public class BeanFactoryPostProcessorService implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryPostProcessorService postProcessBeanFactory method execut...");
BeanDefinition bd = beanFactory.getBeanDefinition("user");
MutablePropertyValues pv = bd.getPropertyValues();
if(pv.contains("userName"))
{
pv.addPropertyValue("userName", "heihei");
}
}
}
總結(jié)
以上就是本文關(guān)于Spring生命周期回調(diào)與容器擴(kuò)展詳解的全部內(nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:
springMVC攔截器HandlerInterceptor用法代碼示例
如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!
相關(guān)文章
SpringBoot3+ShardingJDBC5.5.0 讀寫分離配置的實(shí)現(xiàn)
本文主要介紹了SpringBoot3+ShardingJDBC5.5.0 讀寫分離配置的實(shí)現(xiàn),最新版5.5.0支持SpringBoot3x現(xiàn)分享給大家,具有一定的參考價(jià)值,感興趣的可以了解一下2024-08-08
Java中的任務(wù)調(diào)度框架quartz詳細(xì)解析
這篇文章主要介紹了Java中的任務(wù)調(diào)度框架quartz詳細(xì)解析,Quartz 是一個(gè)完全由 Java 編寫的開源作業(yè)調(diào)度框架,為在 Java 應(yīng)用程序中進(jìn)行作業(yè)調(diào)度提供了簡單卻強(qiáng)大的機(jī)制,需要的朋友可以參考下2023-11-11
Java嵌入式開發(fā)的優(yōu)勢及有點(diǎn)總結(jié)
在本篇內(nèi)容里小編給大家整理了關(guān)于Java嵌入式開發(fā)的優(yōu)勢及相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友們學(xué)習(xí)下。2022-11-11
Spring Cloud Gateway 獲取請(qǐng)求體(Request Body)的多種方法
這篇文章主要介紹了Spring Cloud Gateway 獲取請(qǐng)求體(Request Body)的多種方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
詳解Spring mvc DispatchServlet 實(shí)現(xiàn)機(jī)制
本篇文章主要介紹了詳解Spring mvc DispatchServlet 實(shí)現(xiàn)機(jī)制,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09

