詳解spring——IOC之分析Bean的生命周期

在分析 Spring Bean 實(shí)例化過程中提到 Spring 并不是一啟動(dòng)容器就開啟 bean 的實(shí)例化進(jìn)程,只有當(dāng)客戶端通過顯示或者隱式的方式調(diào)用 BeanFactory 的 getBean() 方法來請(qǐng)求某個(gè)實(shí)例對(duì)象的時(shí)候,它才會(huì)觸發(fā)相應(yīng) bean 的實(shí)例化進(jìn)程,當(dāng)然也可以選擇直接使用 ApplicationContext 容器,因?yàn)樵撊萜鲉?dòng)的時(shí)候會(huì)立刻調(diào)用注冊(cè)到該容器所有 bean 定義的實(shí)例化方法。當(dāng)然對(duì)于 BeanFactory 容器而言并不是所有的 getBean() 方法都會(huì)觸發(fā)實(shí)例化進(jìn)程,比如 signleton 類型的 bean,該類型的 bean 只會(huì)在第一次調(diào)用 getBean() 的時(shí)候才會(huì)觸發(fā),而后續(xù)的調(diào)用則會(huì)直接返回容器緩存中的實(shí)例對(duì)象。

getBean() 只是 bean 實(shí)例化進(jìn)程的入口,真正的實(shí)現(xiàn)邏輯其實(shí)是在 AbstractAutowireCapableBeanFactory 的 doCreateBean() 實(shí)現(xiàn),實(shí)例化過程如下圖:

原來我們采用 new 的方式創(chuàng)建一個(gè)對(duì)象,用完該對(duì)象在其脫離作用域后就會(huì)被回收,對(duì)于后續(xù)操作我們無權(quán)也沒法干涉,但是采用 Spring 容器后,我們完全擺脫了這種命運(yùn),Spring 容器將會(huì)對(duì)其所有管理的 Bean 對(duì)象全部給予一個(gè)統(tǒng)一的生命周期管理,同時(shí)在這個(gè)階段我們也可以對(duì)其進(jìn)行干涉(比如對(duì) bean 進(jìn)行增強(qiáng)處理,對(duì) bean 進(jìn)行篡改),如上圖。

bean 實(shí)例化


在 doCreateBean() 中首先進(jìn)行 bean 實(shí)例化工作,主要由 createBeanInstance() 實(shí)現(xiàn),該方法返回一個(gè) BeanWrapper 對(duì)象。BeanWrapper 對(duì)象是 Spring 的一個(gè)低級(jí) Bean 基礎(chǔ)結(jié)構(gòu)的核心接口,為什么說是低級(jí)呢?因?yàn)檫@個(gè)時(shí)候的 Bean 還不能夠被我們使用,連最基本的屬性都沒有設(shè)置。而且在我們實(shí)際開發(fā)過程中一般都不會(huì)直接使用該類,而是通過 BeanFactory 隱式使用。

BeanWrapper 接口有一個(gè)默認(rèn)實(shí)現(xiàn)類 BeanWrapperImpl,其主要作用是對(duì) Bean 進(jìn)行“包裹”,然后對(duì)這個(gè)包裹的 bean 進(jìn)行操作,比如后續(xù)注入 bean 屬性。

在實(shí)例化 bean 過程中,Spring 采用“策略模式”來決定采用哪種方式來實(shí)例化 bean,一般有反射和 CGLIB 動(dòng)態(tài)字節(jié)碼兩種方式。

InstantiationStrategy 定義了 Bean 實(shí)例化策略的抽象接口,其子類 SimpleInstantiationStrategy 提供了基于反射來實(shí)例化對(duì)象的功能,但是不支持方法注入方式的對(duì)象實(shí)例化。CglibSubclassingInstantiationStrategy 繼承 SimpleInstantiationStrategy,他除了擁有父類以反射實(shí)例化對(duì)象的功能外,還提供了通過 CGLIB 的動(dòng)態(tài)字節(jié)碼的功能進(jìn)而支持方法注入所需的對(duì)象實(shí)例化需求。默認(rèn)情況下,Spring 采用 CglibSubclassingInstantiationStrategy。

激活 Aware


當(dāng) Spring 完成 bean 對(duì)象實(shí)例化并且設(shè)置完相關(guān)屬性和依賴后,則會(huì)開始 bean 的初始化進(jìn)程(initializeBean()),初始化第一個(gè)階段是檢查當(dāng)前 bean 對(duì)象是否實(shí)現(xiàn)了一系列以 Aware 結(jié)尾的的接口。

Aware 接口為 Spring 容器的核心接口,是一個(gè)具有標(biāo)識(shí)作用的超級(jí)接口,實(shí)現(xiàn)了該接口的 bean 是具有被 Spring 容器通知的能力,通知的方式是采用回調(diào)的方式。

在初始化階段主要是感知 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:對(duì)該 bean 對(duì)象定義的 beanName 設(shè)置到當(dāng)前對(duì)象實(shí)例中
BeanClassLoaderAware:將當(dāng)前 bean 對(duì)象相應(yīng)的 ClassLoader 注入到當(dāng)前對(duì)象實(shí)例中
BeanFactoryAware:BeanFactory 容器會(huì)將自身注入到當(dāng)前對(duì)象實(shí)例中,這樣當(dāng)前對(duì)象就會(huì)擁有一個(gè) BeanFactory 容器的引用。
當(dāng)然,Spring 不僅僅只是提供了上面三個(gè) Aware 接口,而是一系列:

  • LoadTimeWeaverAware:加載Spring Bean時(shí)織入第三方模塊,如AspectJ
  • BootstrapContextAware:資源適配器BootstrapContext,如JCA,CCI
  • ResourceLoaderAware:底層訪問資源的加載器
  • PortletConfigAware:PortletConfig
  • PortletContextAware:PortletContext
  • ServletConfigAware:ServletConfig
  • ServletContextAware:ServletContext
  • MessageSourceAware:國際化
  • ApplicationEventPublisherAware:應(yīng)用事件
  • NotificationPublisherAware:JMX通知

BeanPostProcessor


初始化第二個(gè)階段則是 BeanPostProcessor 增強(qiáng)處理,在該階段 BeanPostProcessor 會(huì)處理當(dāng)前容器內(nèi)所有符合條件的實(shí)例化后的 bean 對(duì)象。它主要是對(duì) Spring 容器提供的 bean 實(shí)例對(duì)象進(jìn)行有效的擴(kuò)展,允許 Spring 在初始化 bean 階段對(duì)其進(jìn)行定制化修改,如處理標(biāo)記接口或者為其提供代理實(shí)現(xiàn)。

BeanPostProcessor 接口提供了兩個(gè)方法,在不同的時(shí)機(jī)執(zhí)行,分別對(duì)應(yīng)上圖的前置處理和后置處理。

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 是一個(gè)接口,它為 Spring Bean 的初始化提供了一種方式,它有一個(gè) afterPropertiesSet() 方法,在 bean 的初始化進(jìn)程中會(huì)判斷當(dāng)前 bean 是否實(shí)現(xiàn)了 InitializingBean,如果實(shí)現(xiàn)了則調(diào)用 afterPropertiesSet() 進(jìn)行初始化工作。然后再檢查是否也指定了 init-method(),如果指定了則通過反射機(jī)制調(diào)用指定的 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);
        }
    }
}

對(duì)于 Spring 而言,雖然上面兩種方式都可以實(shí)現(xiàn)初始化定制化,但是更加推崇 init-method 方式,因?yàn)閷?duì)于 InitializingBean 接口而言,他需要 bean 去實(shí)現(xiàn)接口,這樣就會(huì)污染我們的應(yīng)用程序,顯得 Spring 具有一定的侵入性。但是由于 init-method 是采用反射的方式,所以執(zhí)行效率上相對(duì)于 InitializingBean 接口回調(diào)的方式可能會(huì)低一些。

DisposableBean 和 destroy-method


與 InitializingBean 和 init-method 用于對(duì)象的自定義初始化工作相似,DisposableBean和 destroy-method 則用于對(duì)象的自定義銷毀工作。

當(dāng)一個(gè) bean 對(duì)象經(jīng)歷了實(shí)例化、設(shè)置屬性、初始化階段,那么該 bean 對(duì)象就可以供容器使用了(調(diào)用的過程)。當(dāng)完成調(diào)用后,如果是 singleton 類型的 bean ,則會(huì)看當(dāng)前 bean 是否應(yīng)實(shí)現(xiàn)了 DisposableBean 接口或者配置了 destroy-method 屬性,如果是的話,則會(huì)為該實(shí)例注冊(cè)一個(gè)用于對(duì)象銷毀的回調(diào)方法,便于在這些 singleton 類型的 bean 對(duì)象銷毀之前執(zhí)行銷毀邏輯。

但是,并不是對(duì)象完成調(diào)用后就會(huì)立刻執(zhí)行銷毀方法,因?yàn)檫@個(gè)時(shí)候 Spring 容器還處于運(yùn)行階段,只有當(dāng) Spring 容器關(guān)閉的時(shí)候才會(huì)去調(diào)用。但是, Spring 容器不會(huì)這么聰明會(huì)自動(dòng)去調(diào)用這些銷毀方法,而是需要我們主動(dòng)去告知 Spring 容器。

  • 對(duì)于 BeanFactory 容器而言,我們需要主動(dòng)調(diào)用 destroySingletons() 通知 BeanFactory 容器去執(zhí)行相應(yīng)的銷毀方法。
  • 對(duì)于 ApplicationContext 容器而言調(diào)用 registerShutdownHook() 方法。

實(shí)踐驗(yàn)證


下面用一個(gè)實(shí)例來真實(shí)看看看上面執(zhí)行的邏輯,畢竟理論是不能缺少實(shí)踐的:

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("構(gòu)造函數(shù)調(diào)用...");
    }

    public void display(){
        System.out.println("方法調(diào)用...");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactoryAware 被調(diào)用...");
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("BeanNameAware 被調(diào)用...");
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("BeanClassLoaderAware 被調(diào)用...");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor postProcessBeforeInitialization 被調(diào)用...");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor postProcessAfterInitialization 被調(diào)用...");
        return bean;
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean destroy 被調(diào)動(dòng)...");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean afterPropertiesSet 被調(diào)動(dòng)...");
    }

    public void initMethod(){
        System.out.println("init-method 被調(diào)用...");
    }

    public void destroyMethdo(){
        System.out.println("destroy-method 被調(diào)用...");
    }

}

lifeCycleBean 繼承了 BeanNameAware , BeanFactoryAware , BeanClassLoaderAware , BeanPostProcessor ,
InitializingBean , DisposableBean 六個(gè)接口,同時(shí)定義了一個(gè) test 屬性用于驗(yàn)證屬性注入和提供一個(gè) display() 用于模擬調(diào)用。 配置如下:

<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。測(cè)試方法如下:

// BeanFactory 容器一定要調(diào)用該方法進(jìn)行 BeanPostProcessor 注冊(cè)
factory.addBeanPostProcessor(new lifeCycleBean());

lifeCycleBean lifeCycleBean = (lifeCycleBean) factory.getBean("lifeCycle");
lifeCycleBean.display();

System.out.println("方法調(diào)用完成,容器開始關(guān)閉....");
// 關(guān)閉容器
factory.destroySingletons();

運(yùn)行結(jié)果:

構(gòu)造函數(shù)調(diào)用...
構(gòu)造函數(shù)調(diào)用...
屬性注入....
BeanNameAware 被調(diào)用...
BeanClassLoaderAware 被調(diào)用...
BeanFactoryAware 被調(diào)用...
BeanPostProcessor postProcessBeforeInitialization 被調(diào)用...
InitializingBean afterPropertiesSet 被調(diào)動(dòng)...
init-method 被調(diào)用...
BeanPostProcessor postProcessAfterInitialization 被調(diào)用...
方法調(diào)用...
方法調(diào)用完成,容器開始關(guān)閉....
DisposableBean destroy 被調(diào)動(dòng)...
destroy-method 被調(diào)用...

有兩個(gè)構(gòu)造函數(shù)調(diào)用是因?yàn)橐⑷胍粋€(gè) BeanPostProcessor(你也可以另外提供一個(gè) BeanPostProcessor 實(shí)例)。

根據(jù)執(zhí)行的結(jié)果已經(jīng)上面的分析,我們就可以對(duì) Spring Bean 的聲明周期過程如下(方法級(jí)別)

  • Spring 容器根據(jù)實(shí)例化策略對(duì) Bean 進(jìn)行實(shí)例化。
  • 實(shí)例化完成后,如果該 bean 設(shè)置了一些屬性的話,則利用 set 方法設(shè)置一些屬性。
  • 如果該 Bean 實(shí)現(xiàn)了 BeanNameAware 接口,則調(diào)用 setBeanName() 方法。
  • 如果該 bean 實(shí)現(xiàn)了 BeanClassLoaderAware 接口,則調(diào)用 setBeanClassLoader() 方法。
  • 如果該 bean 實(shí)現(xiàn)了 BeanFactoryAware接口,則調(diào)用 setBeanFactory() 方法。
  • 如果該容器注冊(cè)了 BeanPostProcessor,則會(huì)調(diào)用postProcessBeforeInitialization() 方法完成 bean 前置處理
  • 如果該 bean 實(shí)現(xiàn)了 InitializingBean 接口,則調(diào)用 。afterPropertiesSet() 方法。
  • 如果該 bean 配置了 init-method 方法,則調(diào)用 init-method 指定的方法。
  • 初始化完成后,如果該容器注冊(cè)了 BeanPostProcessor 則會(huì)調(diào)用 postProcessAfterInitialization() 方法完成 bean 的后置處理。
  • 對(duì)象完成初始化,開始方法調(diào)用。
  • 在容器進(jìn)行關(guān)閉之前,如果該 bean 實(shí)現(xiàn)了 DisposableBean 接口,則調(diào)用 destroy() 方法。
  • 在容器進(jìn)行關(guān)閉之前,如果該 bean 配置了 destroy-mehod,則調(diào)用其指定的方法。
  • 到這里一個(gè) bean 也就完成了它的一生。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容