在分析 Spring Bean 實例化過程當中提到 Spring 並非一啓動容器就開啓 bean 的實例化進程,只有當客戶端經過顯示或者隱式的方式調用 BeanFactory 的 getBean() 方法來請求某個實例對象的時候,它纔會觸發相應 bean 的實例化進程,固然也能夠選擇直接使用 ApplicationContext 容器,由於該容器啓動的時候會馬上調用註冊到該容器全部 bean 定義的實例化方法。固然對於 BeanFactory 容器而言並非全部的 getBean() 方法都會觸發實例化進程,好比 signleton 類型的 bean,該類型的 bean 只會在第一次調用 getBean() 的時候纔會觸發,然後續的調用則會直接返回容器緩存中的實例對象。spring
getBean() 只是 bean 實例化進程的入口,真正的實現邏輯實際上是在 AbstractAutowireCapableBeanFactory 的 doCreateBean() 實現,實例化過程以下圖:緩存
原來咱們採用 new 的方式建立一個對象,用完該對象在其脫離做用域後就會被回收,對於後續操做咱們無權也無法干涉,可是採用 Spring 容器後,咱們徹底擺脫了這種命運,Spring 容器將會對其全部管理的 Bean 對象所有給予一個統一的生命週期管理,同時在這個階段咱們也能夠對其進行干涉(好比對 bean 進行加強處理,對 bean 進行篡改),如上圖。bash
在 doCreateBean() 中首先進行 bean 實例化工做,主要由 createBeanInstance() 實現,該方法返回一個 BeanWrapper 對象。BeanWrapper 對象是 Spring 的一個低級 Bean 基礎結構的核心接口,爲何說是低級呢?由於這個時候的 Bean 還不可以被咱們使用,連最基本的屬性都沒有設置。並且在咱們實際開發過程當中通常都不會直接使用該類,而是經過 BeanFactory 隱式使用。app
BeanWrapper 接口有一個默認實現類 BeanWrapperImpl,其主要做用是對 Bean 進行「包裹」,而後對這個包裹的 bean 進行操做,好比後續注入 bean 屬性。ide
在實例化 bean 過程當中,Spring 採用「策略模式」來決定採用哪一種方式來實例化 bean,通常有反射和 CGLIB 動態字節碼兩種方式。函數
InstantiationStrategy 定義了 Bean 實例化策略的抽象接口,其子類 SimpleInstantiationStrategy 提供了基於反射來實例化對象的功能,可是不支持方法注入方式的對象實例化。CglibSubclassingInstantiationStrategy 繼承 SimpleInstantiationStrategy,他除了擁有父類以反射實例化對象的功能外,還提供了經過 CGLIB 的動態字節碼的功能進而支持方法注入所需的對象實例化需求。默認狀況下,Spring 採用 CglibSubclassingInstantiationStrategy。post
當 Spring 完成 bean 對象實例化而且設置完相關屬性和依賴後,則會開始 bean 的初始化進程(initializeBean()),初始化第一個階段是檢查當前 bean 對象是否實現了一系列以 Aware 結尾的的接口。測試
Aware 接口爲 Spring 容器的核心接口,是一個具備標識做用的超級接口,實現了該接口的 bean 是具備被 Spring 容器通知的能力,通知的方式是採用回調的方式。ui
在初始化階段主要是感知 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware :this
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 接口,而是一系列:
初始化第二個階段則是 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 是一個接口,它爲 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 接口回調的方式可能會低一些。
與 InitializingBean 和 init-method 用於對象的自定義初始化工做類似,DisposableBean和 destroy-method 則用於對象的自定義銷燬工做。
當一個 bean 對象經歷了實例化、設置屬性、初始化階段,那麼該 bean 對象就能夠供容器使用了(調用的過程)。當完成調用後,若是是 singleton 類型的 bean ,則會看當前 bean 是否應實現了 DisposableBean 接口或者配置了 destroy-method 屬性,若是是的話,則會爲該實例註冊一個用於對象銷燬的回調方法,便於在這些 singleton 類型的 bean 對象銷燬以前執行銷燬邏輯。
可是,並非對象完成調用後就會馬上執行銷燬方法,由於這個時候 Spring 容器還處於運行階段,只有當 Spring 容器關閉的時候纔會去調用。可是, Spring 容器不會這麼聰明會自動去調用這些銷燬方法,而是須要咱們主動去告知 Spring 容器。
下面用一個實例來真實看看看上面執行的邏輯,畢竟理論是不能缺乏實踐的:
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 的聲明週期過程以下(方法級別):