本篇主要總結下Spring容器在初始化實例前後,提供的一些回調方法和可擴展點。利用這些方法和擴展點,可以實現在Spring初始化實例前後做一些特殊邏輯處理。
下面主要介紹:
類級別的生命週期初始化回調方法init-method配置、InitializingBean接口和PostConstruct註解
容器級別的擴展BeanPostProcessor接口和BeanFactoryPostProcessor接口
1.類級別生命週期回調
1.1init-method
參照:Springbeanxsdinit-method
init-method是在Spring配置文件中聲明bean的時候的一個配置項。 init-method配置項的值為類中的一個無參方法,但可拋出異常。該方法會在Spring容器實例化對象並設置完屬性值之後被調用。
init-method能實現的功能與InitializingBean接口、PostConstruct註解一致
Spring配置文件及測試類如下:
<bean id = "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...");}}執行結果打印如下:
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接口中聲明了一個方法afterPropertiesSet,該方法會在Spring容器實例化對象並設置完屬性值之後被調用。和上面的init-method實現的功能一致,因此Spring不推薦使用InitializingBean接口。
例子比較簡單,不列出來了
1.3PostConstruct註解
翻譯:Spring官方文檔beans-postconstruct-and-predestroy-annotations
@PostConstruct註解是和init-method、InitializingBean接口實現效果一致的生命週期回調方法
@PostConstructpublic void postConstruct(){ System.out.println("PostConstructService postConstruct method execute...");}總結下上面三個生命週期回調方法init-method、InitializingBean接口、@PostConstruct註解
1.都是針對單個類的實例化後處理
2.執行時間都是在類實例化完成,且成員變量完成注入之後調用的
3.對於init-method,還可以在Spring配置文件的beans元素下配置默認初始化方法,配置項為default-init-method
4.若以上三種方式配置的初始化方法都不一樣,則執行順序為:@PostConstruct註解方法>InitializingBean的afterPropertiesSet>init-method方法;若三種方式配置的方法一樣,則方法只執行一次(參照:Spring官方文檔beans-factory-lifecycle-combined-effect)
5.有初始化回調方法,對應的也有銷毀的回調方法。 @PostConstruct註解方法>InitializingBean的afterPropertiesSet>init-method方法分別對應@PreDestroy註解方法>DisposableBean的destroy>destroy-method方法
2.容器級別擴展
翻譯:Spring官方文檔3.8ContainerExtensionPoints
通常情況下,開發人員無需自定義實現一個ApplicationContext的子類去擴展SpringIOC容器,SpringIOC容器通過對外暴露的一些接口,可實現對SpringIOC容器的擴展。
2.1BeanPostProcessor接口
2.1.1bean實例初始化後處理器及後處理器鏈
BeanPostProcessor接口定義了兩個容器級別的回調方法postProcessBeforeInitialization和postProcessAfterInitialization,用於在初始化實例後的一些邏輯處理,會針對容器中的所有實例進行處理。實現了BeanPostProcessor接口的類,稱之為bean實例初始化後處理器。
若在SpringIOC容器中集成了多個實例初始化後處理器,這些後處理器構成的集合稱之為bean實例初始化後處理器鏈。
postProcessBeforeInitialization方法在類實例化且成員變量注入完成之後執行,初始化方法(例如InitializingBean的afterPropertiesSet方法)之前執行
postProcessAfterInitialization方法在類實例化且成員變量注入完成之後執行,初始化方法(例如InitializingBean的afterPropertiesSet方法)之後執行
總結:
1.實例初始化後處理器多用於對實例的一些代理操作。 Spring中一些使用到AOP的特性也是通過後處理器的方式實現的。
2.實例初始化後處理器鍊是多個後處理器,就會有執行順序的問題,可以通過實現Ordered接口,指定後處理的執行順序,Ordered接口聲明了getOrder方法,方法返回值越小,後處理的優先級越高,越早執行。
3.在通過實現BeanPostProcessor接口自定義實例初始化後處理器的時候,建議也實現Ordered接口,指定優先級。
4.這些後處理器的作用域是當前的SpringIOC容器,即後處理器被聲明的SpringIOC容器。對於有層次結構的SpringIOC容器,實例初始化後處理器鏈不會作用於其他容器所初始化的實例上,即使兩個容器在同一層次結構上。
5.實例初始化後處理器的實現類只需要和普通的被Spring管理的bean一樣聲明,SpringIOC容器就會自動檢測到,並添加到實例初始化後處理器鏈中。
6.相對於自動檢測,我們也可以調用ConfigurableBeanFactory的addBeanPostProcessor方法,以編程的方式將一個實例初始化後處理器添加到實例初始化後處理器鏈中。這在需要判定添加條件的場景下比較實用。這種編程式的方式會忽略到實現的Ordered接口所指定的順序,而會作用於所有的被自動檢測的實例初始化後處理器之前。
2.1.2bean實例初始化後處理器與AOP
BeanPostProcessor是一個特殊的接口,實現這個接口的類會被作為Spring管理的bean的實例的後處理器。因此,在Spring應用上下文啟動的一個特殊階段,會直接初始化所有實現了BeanPostProcessor接口的實例,以及該實例所引用的類也會被實例化。然後作為後處理器應用於其他普通實例。
由於AOP的自動代理是以實例化後處理器的方式實現的,所以無論是bean實例初始化後處理器鏈實例還是其引用的實例,都不能被自動代理。因而,不要在這些實例上進行切面織入。 (對於這些實例,會產生這樣的日誌消息:“類foo不能被所有的實例化後處理器鏈處理,即不能被自動代理”)。
注意:當實例化後處理器以autowiring或@Resource的方式引用其他bean,Spring容器在以類型匹配依賴注入的時候,可能會注入非指定的bean(例如:實例化後處理器實現類以Resource方式依賴bean,若set方法和被依賴的bean的名稱一致或者被依賴bean未聲明名稱,則依賴注入會以類型匹配的方式註入,此時可能會注入非指定的bean)。這也會導致自動代理或其他方式的實例化後處理器處理失敗。
2.1.3bean實例初始化後處理器示例
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後處理器
通過實現BeanFactoryPostProcessor接口,可以讀取容器所管理的bean的配置元數據,在bean完成實例化之前進行更改,這些bean稱之為beanfactory後處理器。
BeanFactoryPostProcessors與BeanPostProcessor接口的異同點:
相同點:
都是容器級別的後處理器
都可配置多個後處理器,並通過實現Ordered接口,指定執行順序
都是針對接口聲明的容器中所管理的bean進行處理,在有層級結構的容器中,不能處理其他容器中的bean,即使兩個容器是同一層次
都是只需要在容器中和普通bean一樣聲明,容器會自動檢測到,並註冊為後處理器
會忽略延遲初始化屬性配置
不同點:
BeanFactoryPostProcessors接口在bean**實例化前處理bean的配置元數據,BeanPostProcessor接口在bean實例化後處理bean的實例**
BeanFactoryPostProcessors接口也能通過BeanFactory.getBean()方法獲取bean的實例,這樣會引起bean的實例化。由於BeanFactoryPostProcessors後處理器是在所有bean實例化之前執行,通過BeanFactory.getBean()方法會導致提前實例化bean,從而打破容器標準的生命週期,這樣可能會引起一些負面的影響(例如:提前實例化的bean會忽略bean實例化後處理器的處理)。
2.2.2Spring內置及自定義beanfactory後處理器
Spring內置了一些beanfactory後處理器(例如:PropertyPlaceholderConfigurer和PropertyOverrideConfigurer)。同時也支持實現BeanFactoryPostProcessor接口,自定義beanfactory後處理器。下面說說Spring內置的兩個後處理器和自定義後處理器。
PropertyPlaceholderConfigurer
Spring為了避免主要的XML定義文件的修改而引起的風險,提供了配置分離,可以將一些可能變更的變量配置到屬性配置文件中,並在XML定義文件中以佔位符的方式引用。這樣,修改配置只需要修改屬性配置文件即可。 PropertyPlaceholderConfigurer用於檢測佔位符,並替換佔位符為配置屬性值。示例如下:
PropertyPlaceholderConfigurer通過jdbc.properties屬性配置文件,在運行時,將dataSource這個bean中數據庫相關信息的屬性佔位符替換成對應的配置值。
XML配置如下:
<bean> <property name="locations" value="classpath:com/foo/jdbc.properties"/></bean><bean id="dataSource" destroy-method="close" > <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.jdbcDriverjdbc.url=jdbc:hsqldb:hsql://production:9002jdbc.username=sajdbc.password=root
PropertyPlaceholderConfigurer不僅支持屬性配置文件的讀取,也支持讀取系統屬性。通過systemPropertiesMode屬性值可配置讀取優先級。各種取值說明如下:
0:不讀取系統屬性
1:若引用的屬性配置文件中未檢索到對應佔位符的配置,則讀取系統屬性。默認為1
2:先讀取系統屬性,再讀取引用的屬性配置文件。這種配置可能導致系統屬性覆蓋配置文件。
PropertyOverrideConfigurer
PropertyOverrideConfigurer類可以通過引用屬性配置文件,直接給容器中的bean賦值。當一個bean的屬性被多個PropertyOverrideConfigurer類實例賦值時,最後一個的值會覆蓋前面的。
還是以上面給上面的dataSource的bean賦值為例:
PropertyOverrideConfigurer類對屬性配置文件的引用使用一個新的方式,如下:
<context:property-override location="classpath:override.properties"/>
override.properties屬性配置文件的屬性的命名規則和上面不同(上面例子中需要保證屬性名和占位符一致),命名規則是beanName.property
dataSource.driverClassName=com.mysql.jdbc.DriverdataSource.url=jdbc:mysql:mydbdataSource.username=sadataSource.password=root
支持複合屬性的賦值,但是要保證引用被賦值屬性的對象非空例如:foo.fred.bob.sammy=123
自定義bean factory後處理器
自定義bean factory後處理器就是實現BeanFactoryPostProcessor接口,完成對Spring容器管理的bean的配置元數據進行修改。例如:修改類屬性注入的值,示例如下:
定義一個用戶類UserBean
public class UserBean {private String userName;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}}Spring XML配置文件配置用戶類,並給用戶名屬性userName注入值haha
<bean/><bean id="user"> <property name="userName" value="haha"/></bean>
下面是自定義的bean factory後處理器,修改屬性userName的值為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"); } }}總結
以上就是本文關於Spring生命週期回調與容器擴展詳解的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站:
淺談自定義註解在Spring中的應用
Spring的IOC代碼解析
springMVC攔截器HandlerInterceptor用法代碼示例
如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!