在分析 Spring Bean 例項化過程中提到 Spring 並不是一啟動容器就開啟 bean 的例項化程式,只有當客戶端通過顯示或者隱式的方式呼叫 BeanFactory 的 getBean() 方法來請求某個例項物件的時候,它才會觸發相應 bean 的例項化程式,當然也可以選擇直接使用 ApplicationContext 容器,因為該容器啟動的時候會立刻呼叫註冊到該容器所有 bean 定義的例項化方法。當然對於 BeanFactory 容器而言並不是所有的 getBean() 方法都會觸發例項化程式,比如 signleton 型別的 bean,該型別的 bean 只會在第一次呼叫 getBean() 的時候才會觸發,而後續的呼叫則會直接返回容器快取中的例項物件。
getBean() 只是 bean 例項化程式的入口,真正的實現邏輯其實是在 AbstractAutowireCapableBeanFactory 的 doCreateBean() 實現,例項化過程如下圖:
原來我們採用 new 的方式建立一個物件,用完該物件在其脫離作用域後就會被回收,對於後續操作我們無權也沒法干涉,但是採用 Spring 容器後,我們完全擺脫了這種命運,Spring 容器將會對其所有管理的 Bean 物件全部給予一個統一的生命週期管理,同時在這個階段我們也可以對其進行干涉(比如對 bean 進行增強處理,對 bean 進行篡改),如上圖。
bean 例項化
在 doCreateBean() 中首先進行 bean 例項化工作,主要由 createBeanInstance() 實現,該方法返回一個 BeanWrapper 物件。BeanWrapper 物件是 Spring 的一個低階 Bean 基礎結構的核心介面,為什麼說是低階呢?因為這個時候的 Bean 還不能夠被我們使用,連最基本的屬性都沒有設定。而且在我們實際開發過程中一般都不會直接使用該類,而是通過 BeanFactory 隱式使用。
BeanWrapper 介面有一個預設實現類 BeanWrapperImpl,其主要作用是對 Bean 進行“包裹”,然後對這個包裹的 bean 進行操作,比如後續注入 bean 屬性。
在例項化 bean 過程中,Spring 採用“策略模式”來決定採用哪種方式來例項化 bean,一般有反射和 CGLIB 動態位元組碼兩種方式。
InstantiationStrategy 定義了 Bean 例項化策略的抽象介面,其子類 SimpleInstantiationStrategy 提供了基於反射來例項化物件的功能,但是不支援方法注入方式的物件例項化。CglibSubclassingInstantiationStrategy 繼承 SimpleInstantiationStrategy,他除了擁有父類以反射例項化物件的功能外,還提供了通過 CGLIB 的動態位元組碼的功能進而支援方法注入所需的物件例項化需求。預設情況下,Spring 採用 CglibSubclassingInstantiationStrategy。
啟用 Aware
當 Spring 完成 bean 物件例項化並且設定完相關屬性和依賴後,則會開始 bean 的初始化程式(initializeBean()),初始化第一個階段是檢查當前 bean 物件是否實現了一系列以 Aware 結尾的的介面。
Aware 介面為 Spring 容器的核心介面,是一個具有標識作用的超級介面,實現了該介面的 bean 是具有被 Spring 容器通知的能力,通知的方式是採用回撥的方式。
在初始化階段主要是感知 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware :
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
複製程式碼
BeanNameAware:對該 bean 物件定義的 beanName 設定到當前物件例項中 BeanClassLoaderAware:將當前 bean 物件相應的 ClassLoader 注入到當前物件例項中 BeanFactoryAware:BeanFactory 容器會將自身注入到當前物件例項中,這樣當前物件就會擁有一個 BeanFactory 容器的引用。 當然,Spring 不僅僅只是提供了上面三個 Aware 介面,而是一系列:
- LoadTimeWeaverAware:載入Spring Bean時織入第三方模組,如AspectJ
- BootstrapContextAware:資源介面卡BootstrapContext,如JCA,CCI
- ResourceLoaderAware:底層訪問資源的載入器
- PortletConfigAware:PortletConfig
- PortletContextAware:PortletContext
- ServletConfigAware:ServletConfig
- ServletContextAware:ServletContext
- MessageSourceAware:國際化
- ApplicationEventPublisherAware:應用事件
- NotificationPublisherAware:JMX通知
BeanPostProcessor
初始化第二個階段則是 BeanPostProcessor 增強處理,在該階段 BeanPostProcessor 會處理當前容器內所有符合條件的例項化後的 bean 物件。它主要是對 Spring 容器提供的 bean 例項物件進行有效的擴充套件,允許 Spring 在初始化 bean 階段對其進行定製化修改,如處理標記介面或者為其提供代理實現。
BeanPostProcessor 介面提供了兩個方法,在不同的時機執行,分別對應上圖的前置處理和後置處理。
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
複製程式碼
InitializingBean 和 init-method
InitializingBean 是一個介面,它為 Spring Bean 的初始化提供了一種方式,它有一個 afterPropertiesSet() 方法,在 bean 的初始化程式中會判斷當前 bean 是否實現了 InitializingBean,如果實現了則呼叫 afterPropertiesSet() 進行初始化工作。然後再檢查是否也指定了 init-method(),如果指定了則通過反射機制呼叫指定的 init-method()。
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
Boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}
, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
} else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
複製程式碼
對於 Spring 而言,雖然上面兩種方式都可以實現初始化定製化,但是更加推崇 init-method 方式,因為對於 InitializingBean 介面而言,他需要 bean 去實現介面,這樣就會汙染我們的應用程式,顯得 Spring 具有一定的侵入性。但是由於 init-method 是採用反射的方式,所以執行效率上相對於 InitializingBean 介面回撥的方式可能會低一些。
DisposableBean 和 destroy-method
與 InitializingBean 和 init-method 用於物件的自定義初始化工作相似,DisposableBean和 destroy-method 則用於物件的自定義銷燬工作。
當一個 bean 物件經歷了例項化、設定屬性、初始化階段,那麼該 bean 物件就可以供容器使用了(呼叫的過程)。當完成呼叫後,如果是 singleton 型別的 bean ,則會看當前 bean 是否應實現了 DisposableBean 介面或者配置了 destroy-method 屬性,如果是的話,則會為該例項註冊一個用於物件銷燬的回撥方法,便於在這些 singleton 型別的 bean 物件銷燬之前執行銷燬邏輯。
但是,並不是物件完成呼叫後就會立刻執行銷燬方法,因為這個時候 Spring 容器還處於執行階段,只有當 Spring 容器關閉的時候才會去呼叫。但是, Spring 容器不會這麼聰明會自動去呼叫這些銷燬方法,而是需要我們主動去告知 Spring 容器。
- 對於 BeanFactory 容器而言,我們需要主動呼叫 destroySingletons() 通知 BeanFactory 容器去執行相應的銷燬方法。
- 對於 ApplicationContext 容器而言呼叫 registerShutdownHook() 方法。
實踐驗證
下面用一個例項來真實看看看上面執行的邏輯,畢竟理論是不能缺少實踐的:
public class lifeCycleBean implements BeanNameAware,BeanFactoryAware,BeanClassLoaderAware,BeanPostProcessor,
InitializingBean,DisposableBean {
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
System.out.println("屬性注入....");
this.test = test;
}
public lifeCycleBean(){
System.out.println("建構函式呼叫...");
}
public void display(){
System.out.println("方法呼叫...");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware 被呼叫...");
}
@Override
public void setBeanName(String name) {
System.out.println("BeanNameAware 被呼叫...");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("BeanClassLoaderAware 被呼叫...");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor postProcessBeforeInitialization 被呼叫...");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor postProcessAfterInitialization 被呼叫...");
return bean;
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean destroy 被調動...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean afterPropertiesSet 被調動...");
}
public void initMethod(){
System.out.println("init-method 被呼叫...");
}
public void destroyMethdo(){
System.out.println("destroy-method 被呼叫...");
}
}
複製程式碼
lifeCycleBean 繼承了 BeanNameAware , BeanFactoryAware , BeanClassLoaderAware , BeanPostProcessor , InitializingBean , DisposableBean 六個介面,同時定義了一個 test 屬性用於驗證屬性注入和提供一個 display() 用於模擬呼叫。 配置如下:
<bean id="lifeCycle" class="org.springframework.core.test.lifeCycleBean"
init-method="initMethod" destroy-method="destroyMethdo">
<property name="test" value="test"/>
</bean>
複製程式碼
配置 init-method 和 destroy-method。測試方法如下:
// BeanFactory 容器一定要呼叫該方法進行 BeanPostProcessor 註冊
factory.addBeanPostProcessor(new lifeCycleBean());
lifeCycleBean lifeCycleBean = (lifeCycleBean) factory.getBean("lifeCycle");
lifeCycleBean.display();
System.out.println("方法呼叫完成,容器開始關閉....");
// 關閉容器
factory.destroySingletons();
複製程式碼
執行結果:
建構函式呼叫...
建構函式呼叫...
屬性注入....
BeanNameAware 被呼叫...
BeanClassLoaderAware 被呼叫...
BeanFactoryAware 被呼叫...
BeanPostProcessor postProcessBeforeInitialization 被呼叫...
InitializingBean afterPropertiesSet 被調動...
init-method 被呼叫...
BeanPostProcessor postProcessAfterInitialization 被呼叫...
方法呼叫...
方法呼叫完成,容器開始關閉....
DisposableBean destroy 被調動...
destroy-method 被呼叫...
複製程式碼
有兩個建構函式呼叫是因為要注入一個 BeanPostProcessor(你也可以另外提供一個 BeanPostProcessor 例項)。
根據執行的結果已經上面的分析,我們就可以對 Spring Bean 的宣告週期過程如下(方法級別):
- Spring 容器根據例項化策略對 Bean 進行例項化。
- 例項化完成後,如果該 bean 設定了一些屬性的話,則利用 set 方法設定一些屬性。
- 如果該 Bean 實現了 BeanNameAware 介面,則呼叫 setBeanName() 方法。
- 如果該 bean 實現了 BeanClassLoaderAware 介面,則呼叫 setBeanClassLoader() 方法。
- 如果該 bean 實現了 BeanFactoryAware介面,則呼叫 setBeanFactory() 方法。
- 如果該容器註冊了 BeanPostProcessor,則會呼叫postProcessBeforeInitialization() 方法完成 bean 前置處理
- 如果該 bean 實現了 InitializingBean 介面,則呼叫 。afterPropertiesSet() 方法。
- 如果該 bean 配置了 init-method 方法,則呼叫 init-method 指定的方法。
- 初始化完成後,如果該容器註冊了 BeanPostProcessor 則會呼叫 postProcessAfterInitialization() 方法完成 bean 的後置處理。
- 物件完成初始化,開始方法呼叫。
- 在容器進行關閉之前,如果該 bean 實現了 DisposableBean 介面,則呼叫 destroy() 方法。
- 在容器進行關閉之前,如果該 bean 配置了 destroy-mehod,則呼叫其指定的方法。
- 到這裡一個 bean 也就完成了它的一生。