1. 程式人生 > 實用技巧 >Spring Boot -- 啟動流程分析之ApplicationContext 中

Spring Boot -- 啟動流程分析之ApplicationContext 中

上一節我們已經分析到AbsractApplicationContext類refresh方法中的postProcessBeanFactory方法,在分析registerBeanPostProcessors之前我們先介紹一下Spring 的鉤子介面。

@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

一、鉤子介面介紹

Spring 提供了非常多的擴充套件介面,官方將這些介面稱之為鉤子,這些鉤子會在特定的時間被回撥,以此來增強 Spring 功能,眾多優秀的框架也是通過擴充套件這些介面,來實現自身特定的功能,如 SpringBoot、mybatis 等。

二、Aware介面

Aware從字面的意思理解就是"知道"、“感知”的意思,是用來獲取Spring內部物件的介面。Aware自身是一個頂級介面,它有一系列子介面,在一個 Bean 中實現這些子介面並重寫裡面的 set 方法後,Spring 容器啟動時,就會回撥該 set 方法,而相應的物件會通過方法引數傳遞進去。我們以其中的 ApplicationContextAware 介面為例。

2.1、ApplicationContextAware

大部分 Aware 系列介面都有一個規律,它們以物件名稱為字首,獲取的就是該物件,所以 ApplicationContextAware 獲取的物件是 ApplicationContext 。

public interface ApplicationContextAware extends Aware {

    /**
* Set the ApplicationContext that this object runs in.
* Normally this call will be used to initialize the object.
* <p>Invoked after population of normal bean properties but before an init callback such
* as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
* or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
* {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
* {@link MessageSourceAware}, if applicable.
* @param applicationContext the ApplicationContext object to be used by this object
* @throws ApplicationContextException in case of context initialization errors
* @throws BeansException if thrown by application context methods
* @see org.springframework.beans.factory.BeanInitializationException
*/
void setApplicationContext(ApplicationContext applicationContext) throws BeansException; }

ApplicationContextAware 原始碼非常簡單,其繼承了 Aware 介面,並定義一個 set 方法,引數就是 ApplicationContext 物件,當然,其它系列的 Aware 介面也是類似的定義。其具體使用方式如下:

package com.goldwind.spring;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; @Data
@Slf4j
@Component
public class AwareTest implements ApplicationContextAware {
/*
* 儲存應用上下文
*/
private ApplicationContext applicationContext; @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
//輸出所有BeanDefinition name
for(String name:applicationContext.getBeanDefinitionNames()) {
log.info(name);
}
}
}

在 Spring 啟動過程中,會回撥 setApplicationContext 方法,並傳入 ApplicationContext 物件,之後就可對該物件進行操作。我們獲取到ApplicationContext物件,並將所有BeanDefinition名稱輸出:

其它系列的 Aware 介面也是如此使用。具體的呼叫時機會在後面詳細介紹。

以下是幾種常用的 Aware 介面:

  • BeanFactoryAware:獲取 BeanFactory 物件,它是基礎的容器介面。
  • BeanNameAware:獲取 Bean 的名稱。
  • EnvironmentAware:獲取 Environment 物件,它表示整個的執行時環境,可以設定和獲取配置屬性。
  • ApplicationEventPublisherAware:獲取 ApplicationEventPublisher 物件,它是用來發布事件的。
  • ResourceLoaderAware:獲取 ResourceLoader 物件,它是獲取資源的工具。

三、InitializingBean, DisposableBean

3.1、InitializingBean

InitializingBean 是一個可以在 Bean 的生命週期執行自定義操作的介面,凡是實現該介面的 Bean,在初始化階段都可以執行自定義的操作。

public interface InitializingBean {

    /**
* Invoked by the containing {@code BeanFactory} after it has set all bean properties
* and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
* <p>This method allows the bean instance to perform validation of its overall
* configuration and final initialization when all bean properties have been set.
* @throws Exception in the event of misconfiguration (such as failure to set an
* essential property) or if initialization fails for any other reason
*/
void afterPropertiesSet() throws Exception; }

從 InitializingBean 原始碼中可以看出它有一個 afterPropertiesSet 方法,當一個 Bean 實現該介面時,在 Bean 的初始化階段,會回撥 afterPropertiesSet 方法,其初始化階段具體指 Bean 設定完屬性之後。

3.2、DisposableBean

同理,DisposableBean在Bean銷燬時執行自定義的操作,必須資源的釋放。

public interface DisposableBean {

    /**
* Invoked by the containing {@code BeanFactory} on destruction of a bean.
* @throws Exception in case of shutdown errors. Exceptions will get logged
* but not rethrown to allow other beans to release their resources as well.
*/
void destroy() throws Exception; }

比如:

package com.goldwind.spring;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Configuration; @Data
@Configuration
@Slf4j
public class BeanTest implements InitializingBean, DisposableBean {
/*
* 建構函式
*/
public BeanTest(){
log.info("New object...");
} @Override
public void destroy() {
log.info("Destroying ...");
} @Override
public void afterPropertiesSet() {
log.info("Initializing ....");
}
}

四、BeanPostProcessor、BeanFactoryPostProcessor

4.1、BeanPostProcessor

BeanPostProcessor 和 InitializingBean 有點類似,也是可以在 Bean 的生命週期執行自定義操作,一般稱之為 Bean 的後置處理器,不同的是, BeanPostProcessor 可以在 Bean 初始化前、後執行自定義操作,且針對的目標也不同,InitializingBean 針對的是實現 InitializingBean 介面的 Bean,而 BeanPostProcessor 針對的是所有的 Bean。並且postProcessBeforeInitialization在物件建立之後,afterPropertiesSet之前執行,而postProcessAfterInitialization在afterPropertiesSet之後執行:

public interface BeanPostProcessor {

    // Bean 初始化前呼叫
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
} // Bean 初始化後呼叫
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}

所有的 Bean 在初始化前、後都會回撥介面中的 postProcessBeforeInitialization 和 postProcessAfterInitialization 方法,入參是當前正在初始化的 Bean 物件和 BeanName。值得注意的是 Spring 內建了非常多的 BeanPostProcessor ,以此來完善自身功能,這部分會在後面文章深入討論。

我們擴充我們的測試類AwareTest :

package com.goldwind.spring;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; @Data
@Slf4j
@Component
public class AwareTest implements ApplicationContextAware, BeanPostProcessor {
/*
* 儲存應用上下文
*/
private ApplicationContext applicationContext; @Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
//輸出所有BeanDefinition name
for(String name:applicationContext.getBeanDefinitionNames()) {
log.info(name);
}
} @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if(beanName.equals("beanTest")) {
log.info("postProcessBeforeInitialization:" + beanName);
}
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if(beanName.equals("beanTest")) {
log.info("postProcessAfterInitialization:" + beanName);
}
return bean;
} }

BeanTest :

package com.goldwind.spring;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Configuration; @Data
@Configuration
@Slf4j
public class BeanTest implements InitializingBean, DisposableBean, BeanNameAware {
/*
* 儲存當前bean name
*/
private String beanName; /*
* 建構函式
*/
public BeanTest(){
log.info("New object...");
} @Override
public void destroy() {
log.info("Destroying ...");
} @Override
public void afterPropertiesSet() {
log.info("Initializing ....");
} @Override
public void setBeanName(String name) {
this.beanName = name;
log.info("Current bean name:" + name);
}
}

可以看到beanTest物件先是被例項化出來,然後執行BeanPostProcessor的postProcessBeforeInitialization,再執行InitializingBean的afterPropertiesSet,最後執行BeanPostProcessor的postProcessAfterInitialization方法。而ApplicationContextAware的setApplicationContext方法執行時所有BeanDefinition都已載入,但還未例項化Bean

BeanPostProcessor 使用場景其實非常多,因為它可以獲取正在初始化的 Bean 物件,然後可以對Bean 物件做一些定製化的操作,如:判斷該 Bean 是否為某個特定物件、獲取 Bean 的註解元資料等。事實上,Spring 內部也正是這樣使用的,之前我們介紹的Spring Boot -- Spring AOP原理及簡單實現手寫AOP時也是利用了BeanPostProcessor的特性,我們對@Pointcut註解指定的Bean都進行了代理處理。

4.2、BeanFactoryPostProcessor

BeanFactoryPostProcessor 是 Bean 工廠的後置處理器,一般用來修改上下文中的 BeanDefinition,修改 Bean 的屬性值。

public interface BeanFactoryPostProcessor {
// 入參是一個 Bean 工廠:ConfigurableListableBeanFactory。該方法執行時,所有 BeanDefinition 都已被載入,但還未例項化 Bean。
// 可以對其進行覆蓋或新增屬性,甚至可以用於初始化 Bean。
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

BeanFactoryPostProcessor 原始碼非常簡單,其提供了一個 postProcessBeanFactory 方法,當所有的 BeanDefinition 被載入時,該方法會被回撥。值得注意的是,Spring 內建了許多 BeanFactoryPostProcessor 的實現,以此來完善自身功能。 這裡,我們來實現一個自定義的 BeanFactoryPostProcessor:

package com.goldwind.spring;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.stereotype.Component; @Data
@Slf4j
@Component
public class AwareTest implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
log.info("------------------------------------------postProcessBeanFactory-------------------------");
String beanNames[] = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
log.info(beanDefinition.toString());
}
}
}

主要是通過 Bean 工廠獲取所有的 BeanDefinition 。

可以看到,BeanDefinition 正確輸出,裡面是一些 Bean 的相關定義,如:是否懶載入、Bean 的 Class 以及 Bean 的屬性等。

五、ImportSelector

參考文章:

[1]Spring(七)核心容器 - 鉤子介面(轉載)