如何記憶 Spring Bean 的生命週期

1. 引言

「請你描述下 Spring Bean 的生命週期?」,這是面試官考察 Spring 的經常使用問題,可見是 Spring 中很重要的知識點。php

我以前在準備面試時,去網上搜過答案,大多如下圖給出的流程做爲答案。java

Spring的生命週期

可是當我第一次看到該圖時,就產生了不少困擾,「Aware,BeanPostProcessor......這些都是什麼啊?並且這麼多步驟,太多了,該怎麼記啊?」。面試

其實要記憶該過程,仍是須要咱們先去理解,本文將從如下兩方面去幫助理解 Bean 的生命週期:spring

  1. 生命週期的概要流程:對 Bean 的生命週期進行歸納,而且結合代碼來理解;app

  2. 擴展點的做用:詳細介紹 Bean 生命週期中所涉及到的擴展點的做用。post

2. 生命週期的概要流程

Bean 的生命週期歸納起來就是 4 個階段this

  1. 實例化(Instantiation)
  2. 屬性賦值(Populate)
  3. 初始化(Initialization)
  4. 銷燬(Destruction)

  1. 實例化:第 1 步,實例化一個 bean 對象;spa

  2. 屬性賦值:第 2 步,爲 bean 設置相關屬性和依賴;代理

  3. 初始化:第 3~7 步,步驟較多,其中第 五、6 步爲初始化操做,第 三、4 步爲在初始化前執行,第 7 步在初始化後執行,該階段結束,才能被用戶使用;code

  4. 銷燬:第 8~10步,第8步不是真正意義上的銷燬(還沒使用呢),而是先在使用前註冊了銷燬的相關調用接口,爲了後面第九、10步真正銷燬 bean 時再執行相應的方法。

下面咱們結合代碼來直觀的看下,在 doCreateBean() 方法中能看到依次執行了這 4 個階段:

// AbstractAutowireCapableBeanFactory.java
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {

    // 1. 實例化
    BeanWrapper instanceWrapper = null;
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    
    Object exposedObject = bean;
    try {
        // 2. 屬性賦值
        populateBean(beanName, mbd, instanceWrapper);
        // 3. 初始化
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }

    // 4. 銷燬-註冊回調接口
    try {
        registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }

    return exposedObject;
}
複製代碼

因爲初始化包含了第 3~7步,較複雜,因此咱們進到 initializeBean() 方法裏具體看下其過程(註釋的序號對應圖中序號):

// AbstractAutowireCapableBeanFactory.java
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    // 3. 檢查 Aware 相關接口並設置相關依賴
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
    }

    // 4. BeanPostProcessor 前置處理
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    // 5. 若實現 InitializingBean 接口,調用 afterPropertiesSet() 方法
    // 6. 若配置自定義的 init-method方法,則執行
    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }
    // 7. BeanPostProceesor 後置處理
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
}
複製代碼

在 invokInitMethods() 方法中會檢查 InitializingBean 接口和 init-method 方法,銷燬的過程也與其相似:

// DisposableBeanAdapter.java
public void destroy() {
    // 9. 若實現 DisposableBean 接口,則執行 destory()方法
    if (this.invokeDisposableBean) {
        try {
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                    ((DisposableBean) this.bean).destroy();
                    return null;
                }, this.acc);
            }
            else {
                ((DisposableBean) this.bean).destroy();
            }
        }
    }
    
	// 10. 若配置自定義的 detory-method 方法,則執行
    if (this.destroyMethod != null) {
        invokeCustomDestroyMethod(this.destroyMethod);
    }
    else if (this.destroyMethodName != null) {
        Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
        if (methodToInvoke != null) {
            invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
        }
    }
}
複製代碼

從 Spring 的源碼咱們能夠直觀的看到其執行過程,而咱們記憶其過程即可以從這 4 個階段出發,實例化、屬性賦值、初始化、銷燬。其中細節較多的即是初始化,涉及了 Aware、BeanPostProcessor、InitializingBean、init-method 的概念。這些都是 Spring 提供的擴展點,其具體做用將在下一節講述。

3. 擴展點的做用

3.1 Aware 接口

若 Spring 檢測到 bean 實現了 Aware 接口,則會爲其注入相應的依賴。因此經過讓bean 實現 Aware 接口,則能在 bean 中得到相應的 Spring 容器資源

Spring 中提供的 Aware 接口有:

  1. BeanNameAware:注入當前 bean 對應 beanName;
  2. BeanClassLoaderAware:注入加載當前 bean 的 ClassLoader;
  3. BeanFactoryAware:注入 當前BeanFactory容器 的引用。

其代碼實現以下:

// AbstractAutowireCapableBeanFactory.java
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) {
            ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
            
        }
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}
複製代碼

以上是針對 BeanFactory 類型的容器,而對於 ApplicationContext 類型的容器,也提供了 Aware 接口,只不過這些 Aware 接口的注入實現,是經過 BeanPostProcessor 的方式注入的,但其做用還是注入依賴。

  1. EnvironmentAware:注入 Enviroment,通常用於獲取配置屬性;
  2. EmbeddedValueResolverAware:注入 EmbeddedValueResolver(Spring EL解析器),通常用於參數解析;
  3. ApplicationContextAware(ResourceLoader、ApplicationEventPublisherAware、MessageSourceAware):注入 ApplicationContext 容器自己。

其代碼實現以下:

// ApplicationContextAwareProcessor.java
private void invokeAwareInterfaces(Object bean) {
    if (bean instanceof EnvironmentAware) {
        ((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment());
    }

    if (bean instanceof EmbeddedValueResolverAware) {
        ((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver);
    }

    if (bean instanceof ResourceLoaderAware) {
        ((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext);
    }

    if (bean instanceof ApplicationEventPublisherAware) {
        ((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext);
    }

    if (bean instanceof MessageSourceAware) {
        ((MessageSourceAware)bean).setMessageSource(this.applicationContext);
    }

    if (bean instanceof ApplicationContextAware) {
        ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
    }

}
複製代碼

3.2 BeanPostProcessor

BeanPostProcessor 是 Spring 爲修改 bean提供的強大擴展點,其可做用於容器中全部 bean,其定義以下:

public interface BeanPostProcessor {

	// 初始化前置處理
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	// 初始化後置處理
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}
複製代碼

經常使用場景有:

  1. 對於標記接口的實現類,進行自定義處理。例如3.1節中所說的ApplicationContextAwareProcessor,爲其注入相應依賴;再舉個例子,自定義對實現解密接口的類,將對其屬性進行解密處理;
  2. 爲當前對象提供代理實現。例如 Spring AOP 功能,生成對象的代理類,而後返回。
// AbstractAutoProxyCreator.java
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    if (targetSource != null) {
        if (StringUtils.hasLength(beanName)) {
            this.targetSourcedBeans.add(beanName);
        }
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
        this.proxyTypes.put(cacheKey, proxy.getClass());
        // 返回代理類
        return proxy;
    }

    return null;
}
複製代碼

3.3 InitializingBean 和 init-method

InitializingBean 和 init-method 是 Spring 爲 bean 初始化提供的擴展點。

InitializingBean接口 的定義以下:

public interface InitializingBean {
	void afterPropertiesSet() throws Exception;
}
複製代碼

在 afterPropertiesSet() 方法寫初始化邏輯。

指定 init-method 方法,指定初始化方法:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="demo" class="com.chaycao.Demo" init-method="init()"/>
    
</beans>
複製代碼

DisposableBean 和 destory-method 與上述相似,就不描述了。

4. 總結

最後總結下如何記憶 Spring Bean 的生命週期:

  • 首先是實例化、屬性賦值、初始化、銷燬這 4 個大階段;

  • 再是初始化的具體操做,有 Aware 接口的依賴注入、BeanPostProcessor 在初始化先後的處理以及 InitializingBean 和 init-method 的初始化操做;

  • 銷燬的具體操做,有註冊相關銷燬回調接口,最後經過DisposableBean 和 destory-method 進行銷燬。

5. 參考

  1. 請別再問Spring Bean的生命週期了!
  2. 聊聊spring的那些擴展機制

喜歡我文章的小夥伴,能夠掃碼關注下個人公衆號:「草捏子」

相關文章
相關標籤/搜索