Spring Boot -- 啟動流程分析之ApplicationContext 中
- 2020 年 7 月 4 日
- 筆記
- Spring Boot
上一節我們已經分析到AbsractApplicationContext類refresh方法中的postProcessBeanFactory方法,在分析registerBeanPostProcessors之前我們先介紹一下Spring 的鉤子介面,後面我們會在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(七)核心容器 - 鉤子介面(轉載)
