四、原始碼分析 Spring 之IOC 容器的高階特性
高階特性介紹
通過前面對 Spring IOC 容器的原始碼分析,我們已經基本上了解了 Spring IOC 容器對 Bean 定義資源的定位、讀入和解析過程,同時也清楚了當使用者通過 getBean 方法向 IOC 容器獲取被管理的 Bean 時,IOC 容器對 Bean 進行的初始化和依賴注入過程,這些是 Spring IOC 容器的基本功能特性。
Spring IOC 容器還有一些高階特性,如使用 lazy-init 屬性對 Bean 預初始化、FactoryBean 產生或者修飾 Bean 物件的生成、IOC 容器初始化 Bean 過程中使用 BeanPostProcessor 後置處理器對 Bean 宣告週期事件管理和 IOC 容器的 autowiring 自動裝配功能等。
Spring IOC 容器的 lazy-init 屬性實現預例項化
通過前面我們對 IOC 容器的實現和工作原理分析,我們知道 IOC 容器的初始化過程就是對 Bean 定義資源的定位、載入和註冊,此時容器對 Bean 的依賴注入並沒有發生,依賴注入主要是在應用程式第一次向容器索取 Bean 時,通過 getBean 方法的呼叫完成。
當 Bean 定義資源的元素中配置了 lazy-init 屬性時,容器將會在初始化的時候對所配置的 Bean 進行預例項化,Bean 的依賴注入在容器初始化的時候就已經完成。這樣,當應用程式第一次向容器索取被管理的 Bean 時,就不用再初始化和對 Bean 進行依賴注入了,直接從容器中獲取已經完成依賴注入的現成 Bean,可以提高應用第一次向容器獲取 Bean 的效能
1、refresh()
先從 IOC 容器的初始化過程開始,通過前面文章分析,我們知道 IOC 容器讀入已經定位的 Bean 定義資源是從 refresh 方法開始的,我們首先從 AbstractApplicationContext 類的 refresh 方法入手分析。原始碼如下:
//容器初始化的過程,讀入 Bean 定義資源,並解析註冊
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//呼叫容器準備重新整理的方法,獲取容器的當時時間,同時給容器設定同步標識
prepareRefresh();
//告訴子類啟動 refreshBeanFactory()方法,Bean 定義資原始檔的載入從子類的 refreshBeanFactory()方法啟動
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//為 BeanFactory 配置容器特性,例如類載入器、事件處理器等
prepareBeanFactory(beanFactory);
try {
//為容器的某些子類指定特殊的 BeanPost 事件處理器
postProcessBeanFactory(beanFactory);
//呼叫所有註冊的 BeanFactoryPostProcessor 的 Bean
invokeBeanFactoryPostProcessors(beanFactory);
//為 BeanFactory 註冊 BeanPost 事件處理器.
//BeanPostProcessor 是 Bean 後置處理器,用於監聽容器觸發的事件
registerBeanPostProcessors(beanFactory);
//初始化資訊源,和國際化相關.
initMessageSource();
//初始化容器事件傳播器.
initApplicationEventMulticaster();
//呼叫子類的某些特殊 Bean 初始化方法
onRefresh();
//為事件傳播器註冊事件監聽器.
registerListeners();
//這裡是對容器 lazy-init 屬性進行處理的入口方法 ————> 2
finishBeanFactoryInitialization(beanFactory);
//初始化容器的生命週期事件處理器,併發布容器的生命週期事件
finishRefresh();
} catch (BeansException ex) {
//銷燬以建立的單態 Bean
destroyBeans();
//取消 refresh 操作,重置容器的同步標識.
cancelRefresh(ex);
throw ex;
}
}
}
在 refresh 方法中 ConfigurableListableBeanFactorybeanFactory = obtainFreshBeanFactory();啟動了 Bean 定義資源的載入、註冊過程,而 finishBeanFactoryInitialization 方法是對註冊後的 Bean定義中的預例項化(lazy-init = false,Spring 預設就是預例項化,即為 true)的 Bean 進行處理的地方。
2、finishBeanFactoryInitialization 處理預例項化 Bean
當Bean定義資源被載入IOC容器之後,容器將Bean定義資源解析為容器內部的資料結構BeanDefinition註冊到容器中,AbstractApplicationContext 類中的 finishBeanFactoryInitialization 方法對配置了預例項化屬性的 Bean 進行預初始化過程,原始碼如下:
//對配置了 lazy-init 屬性的 Bean 進行預例項化處理
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//這是 Spring3 以後新加的程式碼,為容器指定一個轉換服務(ConversionService)
//在對某些 Bean 屬性進行轉換時使用
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
//為了型別匹配,停止使用臨時的類載入器
beanFactory.setTempClassLoader(null);
//快取容器中所有註冊的 BeanDefinition 元資料,以防被修改
beanFactory.freezeConfiguration();
//對配置了 lazy-init 屬性的單態模式 Bean 進行預例項化處理 ————> 3
beanFactory.preInstantiateSingletons();
}
ConfigurableListableBeanFactory 是一個介面,其 preInstantiateSingletons 方法由其子類DefaultListableBeanFactory 提供。
3、DefaultListableBeanFactory 對配置 lazy-init 屬性單態 Bean 的預例項化
//對配置 lazy-init 屬性單態 Bean 的預例項化
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isInfoEnabled()) {
this.logger.info("Pre-instantiating singletons in " + this);
}
//在對配置 lazy-init 屬性單態 Bean 的預例項化過程中,必須多執行緒同步,以確保資料一致性
synchronized (this.beanDefinitionMap) {
for (String beanName : this.beanDefinitionNames) {
//獲取指定名稱的 Bean 定義
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
//Bean 不是抽象的,是單態模式的,且 lazy-init 屬性配置為 false
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//如果指定名稱的 bean 是建立容器的 Bean
if (isFactoryBean(beanName)) {
//FACTORY_BEAN_PREFIX=”&”,當 Bean 名稱前面加”&”符號時,獲取的是產生容器物件本身,而不是容器產生的 Bean.
//呼叫 getBean 方法,觸發容器對 Bean 例項化和依賴注入過程
final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
//標識是否需要預例項化
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
//一個匿名內部類
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
public Boolean run() {
return ((SmartFactoryBean) factory).isEagerInit();
}
}, getAccessControlContext());
} else {
isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)
factory).isEagerInit();
}
if (isEagerInit) {
//呼叫 getBean 方法,觸發容器對 Bean 例項化和依賴注入過程
getBean(beanName);
}
} else {
//呼叫 getBean 方法,觸發容器對 Bean 例項化和依賴注入過程
getBean(beanName);
}
}
}
}
}
通過對 lazy-init 處理原始碼的分析,我們可以看出,如果設定了 lazy-init 屬性,則容器在完成 Bean 定義的註冊之後,會通過 getBean 方法,觸發對指定 Bean 的初始化和依賴注入過程,這樣當應用第一次向容器索取所需的 Bean 時,容器不再需要對 Bean 進行初始化和依賴注入,直接從已經完成例項化和依賴注入的 Bean 中取一個現成的 Bean,這樣就提高了第一次獲取 Bean 的效能。
FactoryBean 的實現
在 Spring 中,有兩個很容易混淆的類:BeanFactory 和 FactoryBean。
BeanFactory:Bean 工廠,是一個工廠(Factory),我們 Spring IOC 容器的最頂層介面就是這個BeanFactory,它的作用是管理 Bean,即例項化、定位、配置應用程式中的物件及建立這些物件間的依賴。
FactoryBean:工廠 Bean,是一個 Bean,作用是產生其他 bean 例項。通常情況下,這種 bean 沒有什麼特別的要求,僅需要提供一個工廠方法,該方法用來返回其他 bean 例項。通常情況下,bean 無須自己實現工廠模式,Spring 容器擔任工廠角色;但少數情況下,容器中的 bean 本身就是工廠,其作用是產生其它 bean 例項。
當用戶使用容器本身時,可以使用轉義字元”&”來得到 FactoryBean 本身,以區別通過 FactoryBean產生的例項物件和 FactoryBean 物件本身。在 BeanFactory 中通過如下程式碼定義了該轉義字元:StringFACTORY_BEAN_PREFIX = “&”;
如果 myJndiObject 是一個 FactoryBean,則使用&myJndiObject 得到的是 myJndiObject 物件,而不是myJndiObject 產生出來的物件。
1、FactoryBean 的原始碼如下
//工廠 Bean,用於產生其他物件
public interface FactoryBean<T> {
//獲取容器管理的物件例項
T getObject() throws Exception;
//獲取 Bean 工廠建立的物件的型別
Class<?> getObjectType();
//Bean 工廠建立的物件是否是單態模式,如果是單態模式,則整個容器中只有一個例項物件,每次請求都返回同一個例項物件
boolean isSingleton();
}
2、AbstractBeanFactory 的 getBean 方法呼叫 FactoryBean
在前面我們分析 Spring IOC 容器例項化 Bean 並進行依賴注入過程的原始碼時,提到在 getBean 方法觸發容器例項化 Bean 的時候會呼叫 AbstractBeanFactory 的 doGetBean 方法來進行例項化的過程,原始碼如下:
//真正實現向 IOC 容器獲取 Bean 的功能,也是觸發依賴注入功能的地方
@SuppressWarnings("unchecked")
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)throws BeansException {
//根據指定的名稱獲取被管理 Bean 的名稱,剝離指定名稱中對容器的相關依賴
//如果指定的是別名,將別名轉換為規範的 Bean 名稱
final String beanName = transformedBeanName(name);
Object bean;
//先從快取中取是否已經有被建立過的單態型別的 Bean,對於單態模式的 Bean 整個 IoC 容器中只建立一次,不需要重複建立
Object sharedInstance = getSingleton(beanName);
//IoC 容器建立單態模式 Bean 例項物件
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
//如果指定名稱的 Bean 在容器中已有單態模式的 Bean 被建立,直接返回已經建立的 Bean
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference");
} else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//獲取給定 Bean 的例項物件,主要是完成 FactoryBean 的相關處理 ————> 2
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
……
}
//獲取給定 Bean 的例項物件,主要是完成 FactoryBean 的相關處理
protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
//容器已經得到了 Bean 例項物件,這個例項物件可能是一個普通的 Bean,也可能是一個工廠 Bean,
//如果是一個工廠 Bean,則使用它建立一個 Bean 例項物件,
//如果呼叫本身就想獲得一個容器的引用,則指定返回這個工廠 Bean 例項物件
//如果指定的名稱是容器的解引用(dereference,即是物件本身而非記憶體地址),
//且 Bean 例項也不是建立 Bean 例項物件的工廠 Bean
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
//如果 Bean 例項不是工廠 Bean,或者指定名稱是容器的解引用,
//呼叫者向獲取對容器的引用,則直接返回當前的 Bean 例項
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
//處理指定名稱不是容器的解引用,或者根據名稱獲取的 Bean 例項物件是一個工廠 Bean
//使用工廠 Bean 建立一個 Bean 的例項物件
Object object = null;
if (mbd == null) {
//從 Bean 工廠快取中獲取給定名稱的 Bean 例項物件
object = getCachedObjectForFactoryBean(beanName);
}
//讓 Bean 工廠生產給定名稱的 Bean 物件例項
if (object == null) {
FactoryBean factory = (FactoryBean) beanInstance;
//如果從 Bean 工廠生產的 Bean 是單態模式的,則快取
if (mbd == null && containsBeanDefinition(beanName)) {
//從容器中獲取指定名稱的 Bean 定義,如果繼承基類,則合併基類相關屬性
mbd = getMergedLocalBeanDefinition(beanName);
}
//如果從容器得到 Bean 定義資訊,並且 Bean 定義資訊不是虛構的,
//則讓工廠 Bean 生產 Bean 例項物件
boolean synthetic = (mbd != null && mbd.isSynthetic());
//呼叫 FactoryBeanRegistrySupport 類的 getObjectFromFactoryBean 方法,
//實現工廠 Bean 生產 Bean 物件例項的過程 ————> 3
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
在上面獲取給定 Bean 的例項物件的 getObjectForBeanInstance 方法中,會呼叫FactoryBeanRegistrySupport 類的 getObjectFromFactoryBean 方法,該方法實現了 Bean 工廠生產 Bean例項物件。
Dereference(解引用):一個在 C/C++ 中應用比較多的術語,在 C++ 中,”*”是解引用符號,而”&”是引用符號,解引用是指變數指向的是所引用物件的本身資料,而不是引用物件的記憶體地址。
3、AbstractBeanFactory 生產 Bean 例項物件
AbstractBeanFactory 類中生產 Bean 例項物件的主要原始碼如下:
//Bean 工廠生產 Bean 例項物件
protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) {
//Bean 工廠是單態模式,並且 Bean 工廠快取中存在指定名稱的 Bean 例項物件
if (factory.isSingleton() && containsSingleton(beanName)) {
//多執行緒同步,以防止資料不一致
synchronized (getSingletonMutex()) {
//直接從 Bean 工廠快取中獲取指定名稱的 Bean 例項物件
Object object = this.factoryBeanObjectCache.get(beanName);
//Bean 工廠快取中沒有指定名稱的例項物件,則生產該例項物件
if (object == null) {
//呼叫 Bean 工廠的 getObject 方法生產指定 Bean 的例項物件 ————> 3
object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
//將生產的例項物件新增到 Bean 工廠快取中
this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
}
return (object != NULL_OBJECT ? object : null);
}
}
//呼叫 Bean 工廠的 getObject 方法生產指定 Bean 的例項物件 ————> 3
else {
return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
}
}
//呼叫 Bean 工廠的 getObject 方法生產指定 Bean 的例項物件
private Object doGetObjectFromFactoryBean(final FactoryBean factory, final String beanName, final boolean shouldPostProcess)throws BeanCreationException {
Object object;
try {
if (System.getSecurityManager() != null) {
AccessControlContext acc = getAccessControlContext();
try {
//實現 PrivilegedExceptionAction 介面的匿名內建類
//根據 JVM 檢查許可權,然後決定 BeanFactory 建立例項物件
object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
public Object run() throws Exception {
//呼叫 BeanFactory 介面實現類的建立物件方法
return factory.getObject();
}
}, acc);
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
} else {
//呼叫 BeanFactory 介面實現類的建立物件方法
object = factory.getObject();
}
} catch (FactoryBeanNotInitializedException ex) {
throw new BeanCurrentlyInCreationException(beanName, ex.toString());
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
//創建出來的例項物件為 null,或者因為單態物件正在建立而返回 null
if (object == null && isSingletonCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException( beanName, "FactoryBean which is currently in creation returned null from getObject");
}
//為創建出來的 Bean 例項物件新增 BeanPostProcessor 後置處理器
if (object != null && shouldPostProcess) {
try {
object = postProcessObjectFromFactoryBean(object, beanName);
} catch (Throwable ex) {
throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);
}
}
return object;
}
從上面的原始碼分析中,我們可以看出,BeanFactory 介面呼叫其實現類的 getObject 方法來實現建立 Bean 例項物件的功能。
4、工廠 Bean 的實現類 getObject 方法建立 Bean 例項物件
FactoryBean的實現類有非常多,比如:Proxy、RMI、JNDI、ServletContextFactoryBean等等,FactoryBean 介面為 Spring 容器提供了一個很好的封裝機制,具體的 getObject 有不同的實現類根據不同的實現策略來具體提供,我們分析一個最簡單的 AnnotationTestFactoryBean 的實現原始碼:
public class AnnotationTestBeanFactory implements FactoryBean<IJmxTestBean> {
private final FactoryCreatedAnnotationTestBean instance = new FactoryCreatedAnnotationTestBean();
public AnnotationTestBeanFactory() {
this.instance.setName("FACTORY");
}
//AnnotationTestBeanFactory 產生 Bean 例項物件的實現
public IJmxTestBean getObject() throws Exception {
return this.instance;
}
public Class<? extends IJmxTestBean> getObjectType() {
return FactoryCreatedAnnotationTestBean.class;
}
public boolean isSingleton() {
return true;
}
}
其他的 Proxy,RMI,JNDI 等等,都是根據相應的策略提供 getObject 的實現。這裡不做一一分析,這已經不是 Spring 的核心功能,有需要的時候再去深入研究。
BeanPostProcessor 後置處理器的實現
BeanPostProcessor 後置處理器是 Spring IOC 容器經常使用到的一個特性,這個 Bean 後置處理器是一個監聽器,可以監聽容器觸發的 Bean 宣告週期事件。後置處理器向容器註冊以後,容器中管理的 Bean就具備了接收 IOC 容器事件回撥的能力。
BeanPostProcessor 的使用非常簡單,只需要提供一個實現介面 BeanPostProcessor 的實現類,然後在Bean 的配置檔案中設定即可。
1、BeanPostProcessor 的原始碼如下
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
public interface BeanPostProcessor {
//為在 Bean 的初始化前提供回撥入口
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//為在 Bean 的初始化之後提供回撥入口
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
這兩個回撥的入口都是和容器管理的 Bean 的生命週期事件緊密相關,可以為使用者提供在 Spring IOC
容器初始化 Bean 過程中自定義的處理操作。
2、AbstractAutowireCapableBeanFactory 類對容器生成的 Bean 新增後置處理器
BeanPostProcessor後置處理器的呼叫發生在Spring IOC容器完成對Bean例項物件的建立和屬性的依賴注入完成之後,在對 Spring 依賴注入的原始碼分析過程中我們知道,當應用程式第一次呼叫 getBean方法(lazy-init 預例項化除外)向 Spring IOC 容器索取指定 Bean 時觸發 Spring IOC 容器建立 Bean例項物件並進行依賴注入的過程,其中真正實現建立 Bean 物件並進行依賴注入的方法是AbstractAutowireCapableBeanFactory 類的 doCreateBean 方法,主要原始碼如下:
//真正建立 Bean 的方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
//建立 Bean 例項物件
……
try {
//對 Bean 屬性進行依賴注入
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
//在對 Bean 例項物件生成和依賴注入完成以後,開始對 Bean 例項物件
//進行初始化 ,為 Bean 例項物件應用 BeanPostProcessor 後置處理器
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
} catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException)ex).getBeanName())) {
throw (BeanCreationException) ex;
}
……
//為應用返回所需要的例項物件
return exposedObject;
}
}
從上面的程式碼中我們知道,為 Bean 例項物件新增 BeanPostProcessor 後置處理器的入口的是
initializeBean 方法。
3、initializeBean 方法為容器產生的 Bean 例項物件新增 BeanPostProcessor 後置處理器
同樣在 AbstractAutowireCapableBeanFactory 類中,initializeBean 方法實現為容器建立的 Bean 例項
物件新增 BeanPostProcessor 後置處理器,原始碼如下:
//初始容器建立的 Bean 例項物件,為其新增 BeanPostProcessor 後置處理器
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd){
//JDK 的安全機制驗證許可權
if (System.getSecurityManager() != null) {
//實現 PrivilegedAction 介面的匿名內部類
AccessController.doPrivileged(new PrivilegedAction<Object>