Spring ApplicationContext上下文核心容器深入探究
Spring 容器核心可歸納為兩個類: BeanFactory 和 ApplicationContext,ApplicationContext 繼承自 BeanFactory ,其不僅包含 BeanFactory 所有功能,還擴(kuò)展了容器功能。ApplicationContext 核心其實(shí)是 refresh 方法,容器一系列功能都在該方法中實(shí)現(xiàn),如:注冊 Bean、注入 Bean 等。
整體流程
refresh 方法定義在 ConfigurableApplicationContext 接口中,被 AbstractApplicationContext 抽象類實(shí)現(xiàn),該方法由十幾個子方法組成,這些子方法各司其職完成容器的初始化。
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
......
//加載或刷新配置的持久表示形式,
//可能是XML文件、屬性文件或關(guān)系數(shù)據(jù)庫模式。
//由于這是一個啟動方法,它應(yīng)該銷毀已經(jīng)創(chuàng)建的單例
//如果失敗,則避免懸而未決的資源。換句話說,在調(diào)用該方法之后,要么全部實(shí)例化,要么根本不實(shí)例化。
//如果無法初始化bean工廠,則拋出BeanException異常,拋出IllegalStateException異常
//如果已初始化且不支持多次刷新嘗試
void refresh() throws BeansException, IllegalStateException;
......
}public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
......
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 準(zhǔn)備此上下文以進(jìn)行刷新
prepareRefresh();
// 告訴子類刷新內(nèi)部bean工廠
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 準(zhǔn)備bean工廠,以便在此上下文中使用
prepareBeanFactory(beanFactory);
try {
// 允許在上下文子類中對bean工廠進(jìn)行后處理
postProcessBeanFactory(beanFactory);
// 調(diào)用在上下文中注冊為bean的工廠處理器
invokeBeanFactoryPostProcessors(beanFactory);
// 注冊攔截bean創(chuàng)建的bean處理器
registerBeanPostProcessors(beanFactory);
// 為此上下文初始化消息源。
initMessageSource();
// 為此上下文初始化事件多播
initApplicationEventMulticaster();
// 初始化特定上下文子類中的其他特殊bean
onRefresh();
// 檢查偵聽器bean并注冊它們
registerListeners();
// 實(shí)例化所有剩余的(非懶加載)單例
finishBeanFactoryInitialization(beanFactory);
// 最后一步:發(fā)布相應(yīng)的事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 銷毀已創(chuàng)建的單例以避免懸空資源
destroyBeans();
// 重置“活動”標(biāo)志
cancelRefresh(ex);
// 將異常傳播到調(diào)用方
throw ex;
}
finally {
// 重置Spring核心中的常見內(nèi)省緩存,因?yàn)槲覀兛赡懿辉傩枰獑卫齜ean的元數(shù)據(jù)。。。
resetCommonCaches();
}
}
}
......
}在spring中AbstractApplicationContext的實(shí)現(xiàn)類通過調(diào)用父類的方法完成容器的初始化,下文以ClassPathXmlApplicationContext為例來追蹤容器的創(chuàng)建流程。
public class contextText {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-bean.xml");
}public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
......
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
......
}refresh 方法中,前四個子方法主要進(jìn)行上下文準(zhǔn)備工作。
prepareRefresh
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 初始化上下文環(huán)境,就是記錄下容器的啟動時間、活動狀態(tài)等
prepareRefresh();
...
}
}public abstract class AbstractApplicationContext {
...
private long startupDate;
private final AtomicBoolean active = new AtomicBoolean();
private final AtomicBoolean closed = new AtomicBoolean();
private Set<ApplicationEvent> earlyApplicationEvents;
...
protected void prepareRefresh() {
// 記錄此上下文開始時的系統(tǒng)時間(以毫秒為單位)
this.startupDate = System.currentTimeMillis();
// 記錄此上下文是否已關(guān)閉,這里設(shè)置為未關(guān)閉
this.closed.set(false);
// 記錄此上下文是否處于活動狀態(tài),這里設(shè)置為活動狀態(tài)
this.active.set(true);
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// 這也是交由子類擴(kuò)展的方法。具體子類為 GenericWebApplicationContext,主要是初始化屬性源,
// 將 ServletContext 和 ServletConfig 屬性配置添加到 Environment 環(huán)境上下文中
initPropertySources();
// 校驗(yàn) Environment 中那些必備的屬性配置是否存在,不存在則拋異常。
getEnvironment().validateRequiredProperties();
// 創(chuàng)建 ApplicationEvent 事件集合
this.earlyApplicationEvents = new LinkedHashSet<>();
}
}refresh 中的 prepareRefresh 方法執(zhí)行結(jié)束,主要是記錄容器的啟動時間、活動狀態(tài)、檢查必備屬性是否存在。
obtainFreshBeanFactory
public abstract class AbstractApplicationContext {
...
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
...
}
}
...
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 該方法也是由子類擴(kuò)展,其子類有 AbstractRefreshableApplicationContext 和 GenericApplicationContext,
// 該方法主要設(shè)置 BeanFactory 的 serializationId 屬性值,也就是序列化id
refreshBeanFactory();
// 通過 getBeanFactory 返回 BeanFactory 對象。同樣也是由子類擴(kuò)展,調(diào)用的是 GenericApplicationContext 類中的 getBeanFactory 方法。
// 返回的是 DefaultListableBeanFactory 。
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
...
}obtainFreshBeanFactory 方法很簡單,因?yàn)楫?dāng)前使用的是ClasspathXmlApplicationContext容器類,從類的繼承關(guān)系可以看出執(zhí)行的就是 AbstractRefreshableApplicationContext 中的 refreshBeanFactory 方法,返回的是DefaultListableBeanFactory,之后該方法還返回了 BeanFactory 對象,從這也可以看出 ApplicationContext 底層是以 BeanFactory 為基礎(chǔ),逐步擴(kuò)展 Spring 容器功能。

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
......
/** Bean factory for this context */
@Nullable
private DefaultListableBeanFactory beanFactory;
......
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
synchronized (this.beanFactoryMonitor) {
if (this.beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - " +
"call 'refresh' before accessing beans via the ApplicationContext");
}
return this.beanFactory;
}
}
......
}prepareBeanFactory
prepareBeanFactory 方法主要是對 BeanFactory 做一些配置,包含各種類加載器、需要忽略的依賴以及后置處理器、解析器等
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
prepareBeanFactory(beanFactory);
...
}
...
}
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 設(shè)置類加載器
beanFactory.setBeanClassLoader(getClassLoader());
// 設(shè)置表達(dá)式解析器,主要用來解析 EL 表達(dá)式; Bean 初始化完成后填充屬性時會用到
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 設(shè)置屬性注冊解析器,主要用來解析 Bean 中的各種屬性類型,如 String、int 等
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 添加一個后置處理器:ApplicationContextAwareProcessor。
// 該后置處理器用于向?qū)崿F(xiàn)了 Aware 系列接口的 bean 設(shè)置相應(yīng)屬性。
// (后置處理器和 Aware 接口也是比較核心的概念,后面會有文章詳細(xì)討論)
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 以下接口,在自動注入時會被忽略,其都是 Aware 系列接口
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// 當(dāng)以下特殊的 Bean 需自動注入時,指定其注入的類型 。
// 如:注入 BeanFactory 時,注入的類型對象為 ConfigurableListableBeanFactory 。
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// 添加 ApplicationListenerDetector 后置處理器。
// 該后置處理器用來檢測那些實(shí)現(xiàn)了 ApplicationListener 接口的 bean,并將其添加到應(yīng)用上下文的事件廣播器上。
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// 判斷容器中是否存在 loadTimeWeaver Bean,如果存在則上下文使用臨時的 ClassLoader 進(jìn)行類型匹配。
// 集成 AspectJ 時會用到 loadTimeWeaver 對象。
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// 注冊和環(huán)境相關(guān)的 Bean,如 environment、systemProperties、systemEnvironment
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}在 prepareBeanFactory 方法中,主要對 BeanFactory 添加了一系列屬性項(xiàng),如添加忽略自動注入的接口、添加 BeanPostProcessor 后置處理器、手動注冊部分特殊的 Bean及環(huán)境相關(guān)的 Bean 。
postProcessBeanFactory
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
postProcessBeanFactory(beanFactory);
...
}
}postProcessBeanFactory 方法是上下文準(zhǔn)備的最后一步,spring中并沒有具體去實(shí)現(xiàn)postProcessBeanFactory方法,是提供給想要實(shí)現(xiàn)BeanPostProcessor的三方框架使用的。誰要使用誰就去實(shí)現(xiàn)。作用是在BeanFactory準(zhǔn)備工作完成后做一些定制化的處理,一般結(jié)合BeanPostProcessor接口的實(shí)現(xiàn)類一起使用,注入一些重要資源(類似Application的屬性和ServletContext的屬性)。最后需要設(shè)置忽略這類BeanPostProcessor子接口的自動裝配。
總結(jié)
ApplicationContext 上下文準(zhǔn)備工作基本結(jié)束,主要還是在 BeanFactory 中添加一系列后置處理器、注冊特殊的 Bean 及設(shè)置忽略自動注入的接口。其中還提到了 Spring 容器的三個核心部分:Aware 系列接口、BeanPostProcessor 后置處理器、BeanDefinition ,這部分在后面的文章會逐步跟蹤。
到此這篇關(guān)于Spring ApplicationContext上下文核心容器深入探究的文章就介紹到這了,更多相關(guān)Spring ApplicationContext內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring系列中的beanFactory與ApplicationContext
- Spring中BeanFactory和ApplicationContext的作用和區(qū)別(推薦)
- ServletWebServerApplicationContext創(chuàng)建Web容器Tomcat示例
- PostConstruct注解標(biāo)記類ApplicationContext未加載空指針
- SpringBoot項(xiàng)目報錯:"Error?starting?ApplicationContext...."解決辦法
- SpringBoot如何使用applicationContext.xml配置文件
- 基于Failed?to?load?ApplicationContext異常的解決思路
- 一文學(xué)透ApplicationContext繼承接口功能及與BeanFactory區(qū)別
相關(guān)文章
基于springboot bean的實(shí)例化過程和屬性注入過程
這篇文章主要介紹了基于springboot bean的實(shí)例化過程和屬性注入過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
云IDE:Eclipse Che:Eclipse下一代IDE(推薦)
這篇文章主要介紹了云IDE:Eclipse Che:Eclipse下一代IDE,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09
java 遍歷request中的所有表單數(shù)據(jù)的實(shí)例代碼
下面小編就為大家?guī)硪黄猨ava 遍歷request中的所有表單數(shù)據(jù)的實(shí)例代碼。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-09-09
java ant包中的org.apache.tools.zip實(shí)現(xiàn)壓縮和解壓縮實(shí)例詳解
這篇文章主要介紹了java ant包中的org.apache.tools.zip實(shí)現(xiàn)壓縮和解壓縮實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04
ReadWriteLock接口及其實(shí)現(xiàn)ReentrantReadWriteLock方法
下面小編就為大家?guī)硪黄猂eadWriteLock接口及其實(shí)現(xiàn)ReentrantReadWriteLock方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06
Java中Lambda表達(dá)式之Lambda語法與作用域解析
這篇文章主要介紹了Java中Lambda表達(dá)式之Lambda語法與作用域解析重點(diǎn)介紹Lambda表達(dá)式基礎(chǔ)知識,需要的朋友可以參考下2017-02-02

