Spring 並不是一啓動容器就開啓 bean 的實例化進程,只有當客戶端通過顯示或者隱式的方式調用 BeanFactory 的 #getBean(...) 方法來請求某個實例對象的時候,它纔會觸發相應 bean 的實例化進程。這也符合懶加載的,也可以選擇直接使用 ApplicationContext 容器,因爲該容器啓動的時候會立刻調用註冊到該容器所有 bean 定義的實例化方法。當然,對於 BeanFactory 容器而言,並不是所有的 #getBean(...) 方法都會觸發實例化進程,比如 singleton 類型的 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) {
// 對該 bean 對象定義的 beanName 設置到當前對象實例中
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
// 將當前 bean 對象相應的 ClassLoader 注入到當前對象實例中
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
// BeanFactory 容器會將自身注入到當前對象實例中,這樣當前對象就會擁有一個 BeanFactory 容器的引用
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
除了上面三個Aware接口,ApplicationContextAware這個接口大家應該比較熟悉在日常開發中使用的頻率還是蠻高的,Spring提供了一系列的Aware接口,感興趣的同學可以自己研究一下。
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 {
// ZHOUJ 2020-02-22 21:41 首先會檢查是否是 InitializingBean ,如果是的話需要調用 afterPropertiesSet()
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("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();
}
}
// ZHOUJ 2020-02-22 21:42 判斷是否指定了 init-method(),如果指定了 init-method(),則再調用制定的init-method
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() 方法。
生命週期驗證
package com.zhouj.endless.spring_ioc.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @author 天啓
* @date 2020-02-22 22:02
* @description
*/
public class LifeCycleBean implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, BeanPostProcessor, InitializingBean, DisposableBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("屬性注入....");
this.name = name;
}
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 被調用...");
}
}
執行測試
@Test
public void lifeCycleBean() {
ClassPathResource resource = new ClassPathResource("bean.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// BeanFactory 容器一定要調用該方法進行 BeanPostProcessor 註冊
factory.addBeanPostProcessor(new LifeCycleBean());
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
LifeCycleBean lifeCycleBean = (LifeCycleBean) factory.getBean("lifeCycleBean");
lifeCycleBean.display();
System.out.println("方法調用完成,容器開始關閉....");
// 關閉容器
factory.destroySingletons();
}
運行結果
根據執行的結果已經上面的分析,我們就可以對 Spring Bean 的聲明週期過程如下(方法級別):
- Spring 容器根據實例化策略對 Bean 進行實例化。
- 實例化完成後,如果該 bean 設置了一些屬性的話,則利用 set 方法設置一些屬性。
- 如果該 Bean 實現了 BeanNameAware 接口,則調用 #setBeanName(String beanName) 方法。
- 如果該 bean 實現了 BeanClassLoaderAware 接口,則調用 setBeanClassLoader(ClassLoader classLoader) 方法。
- 如果該 bean 實現了 BeanFactoryAware接口,則調用 setBeanFactory(BeanFactory beanFactory) 方法。
- 如果該容器註冊了 BeanPostProcessor,則會調用#postProcessBeforeInitialization(Object bean, String beanName) 方法,完成 bean 前置處理
- 如果該 bean 實現了 InitializingBean 接口,則調用#afterPropertiesSet() 方法。
- 如果該 bean 配置了 init-method 方法,則調用其指定的方法。
- 初始化完成後,如果該容器註冊了 BeanPostProcessor 則會調用 #postProcessAfterInitialization(Object bean, String beanName) 方法,完成 bean 的後置處理。
- 對象完成初始化,開始方法調用。
- 在容器進行關閉之前,如果該 bean 實現了 DisposableBean 接口,則調用 #destroy() 方法。
- 在容器進行關閉之前,如果該 bean 配置了 destroy-method ,則調用其指定的方法。
- 到這裏一個 bean 也就完成了它的一生。