Spring IoC之深入分析Bean的生命週期

Spring 並不是一啓動容器就開啓 bean 的實例化進程,只有當客戶端通過顯示或者隱式的方式調用 BeanFactory 的 #getBean(...) 方法來請求某個實例對象的時候,它纔會觸發相應 bean 的實例化進程。這也符合懶加載的,也可以選擇直接使用 ApplicationContext 容器,因爲該容器啓動的時候會立刻調用註冊到該容器所有 bean 定義的實例化方法。當然,對於 BeanFactory 容器而言,並不是所有的 #getBean(...) 方法都會觸發實例化進程,比如 singleton 類型的 bean,該類型的 bean 只會在第一次調用 getBean() 的時候纔會觸發,而後續的調用則會直接返回容器緩存中的實例對象。

#getBean(...) 方法,只是 bean 實例化進程的入口,真正的實現邏輯其實是在 AbstractAutowireCapableBeanFactory 的 #doCreateBean(...) 中實現,實例化過程如下圖:

image.png

原來我們採用 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();
}

運行結果

image.png

根據執行的結果已經上面的分析,我們就可以對 Spring Bean 的聲明週期過程如下(方法級別):

  1. Spring 容器根據實例化策略對 Bean 進行實例化。
  2. 實例化完成後,如果該 bean 設置了一些屬性的話,則利用 set 方法設置一些屬性。
  3. 如果該 Bean 實現了 BeanNameAware 接口,則調用 #setBeanName(String beanName) 方法。
  4. 如果該 bean 實現了 BeanClassLoaderAware 接口,則調用 setBeanClassLoader(ClassLoader classLoader) 方法。
  5. 如果該 bean 實現了 BeanFactoryAware接口,則調用 setBeanFactory(BeanFactory beanFactory) 方法。
  6. 如果該容器註冊了 BeanPostProcessor,則會調用#postProcessBeforeInitialization(Object bean, String beanName) 方法,完成 bean 前置處理
  7. 如果該 bean 實現了 InitializingBean 接口,則調用#afterPropertiesSet() 方法。
  8. 如果該 bean 配置了 init-method 方法,則調用其指定的方法。
  9. 初始化完成後,如果該容器註冊了 BeanPostProcessor 則會調用 #postProcessAfterInitialization(Object bean, String beanName) 方法,完成 bean 的後置處理。
  10. 對象完成初始化,開始方法調用。
  11. 在容器進行關閉之前,如果該 bean 實現了 DisposableBean 接口,則調用 #destroy() 方法。
  12. 在容器進行關閉之前,如果該 bean 配置了 destroy-method ,則調用其指定的方法。
  13. 到這裏一個 bean 也就完成了它的一生。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章