淺析spring——IOC 之 分析 Bean 的生命週期

在分析 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

bean 實例化


在 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

激活 Aware


當 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 接口,而是一系列:

  • 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 也就完成了它的一輩子。
相關文章
相關標籤/搜索