Spring核心容器之ApplicationContext上下文啟動(dòng)準(zhǔn)備詳解
前言
前面介紹了 Spring 容器的概念,其核心可歸納為兩個(gè)類: BeanFactory 和 ApplicationContext,ApplicationContext 繼承自 BeanFactory ,其不僅包含 BeanFactory 所有功能,還擴(kuò)展了容器功能。之后介紹了在 SSM 時(shí)期和 SpringBoot 時(shí)期如何啟動(dòng) ApplicationContext 。在結(jié)尾處,我們指出,ApplicationContext 核心其實(shí)是 refresh 方法,容器一系列功能都在該方法中實(shí)現(xiàn),如:注冊(cè) Bean、注入 Bean 等。
在 refresh 方法中,實(shí)現(xiàn)容器核心功能前,先進(jìn)行了一系列環(huán)境準(zhǔn)備工作,我們以 SpringBoot 為當(dāng)前運(yùn)行環(huán)境,深入討論這部分內(nèi)容。
注:本篇文章使用的 SpringBoot 版本為 2.0.3.RELEASE,其 Spring 版本為 5.0.7.RELEASE
正文
refresh 方法定義在 ConfigurableApplicationContext 接口中,被 AbstractApplicationContext 抽象類實(shí)現(xiàn),該方法由十幾個(gè)子方法組成,這些子方法各司其職,但部分子方法被 AbstractApplicationContext 的子類進(jìn)行擴(kuò)展,來(lái)增強(qiáng)功能。其中,前四個(gè)子方法主要進(jìn)行上下文準(zhǔn)備工作。
第一步:prepareRefresh
我們先從 refresh 中的 prepareRefresh 方法開(kāi)始討論:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 初始化上下文環(huán)境,就是記錄下容器的啟動(dòng)時(shí)間、活動(dòng)狀態(tài)等 prepareRefresh(); ... } }
該方法被繼承 AbstractApplicationContext 抽象類的子類進(jìn)行擴(kuò)展,擴(kuò)展該方法的子類有:
因本次演示的環(huán)境是 SpringBoot ,前面我們講過(guò),SpringBoot 會(huì)根據(jù)當(dāng)前 Web 應(yīng)用類型創(chuàng)建不同的上下文對(duì)象 ,如 Servlet Web、Reactive Web 等。這里演示的是 Servlet Web 應(yīng)用,所以創(chuàng)建的上下文對(duì)象是 AnnotationConfigServletWebServerApplicationContext 。該類的 prepareRefresh 方法會(huì)被執(zhí)行:
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { ... @Override protected void prepareRefresh() { // 清除 Class 的元數(shù)據(jù)緩存。底層用 Map 保存元數(shù)據(jù),執(zhí)行 Map 的 clear 方法 this.scanner.clearCache(); // 調(diào)用父類,也就是 AbstractApplicationContext 的 prepareRefresh 方法 super.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() { // 記錄此上下文開(kāi)始時(shí)的系統(tǒng)時(shí)間(以毫秒為單位) this.startupDate = System.currentTimeMillis(); // 記錄此上下文是否已關(guān)閉,這里設(shè)置為未關(guān)閉 this.closed.set(false); // 記錄此上下文是否處于活動(dòng)狀態(tài),這里設(shè)置為活動(dòng)狀態(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é)束,主要是記錄容器的啟動(dòng)時(shí)間、活動(dòng)狀態(tài)、檢查必備屬性是否存在。
第二步:obtainFreshBeanFactory
接著進(jìn)入 refresh 中的 obtainFreshBeanFactory 方法
public abstract class AbstractApplicationContext { ... public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { ... ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); ... } } ... protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 該方法也是由子類擴(kuò)展,其子類有 AbstractRefreshableApplicationContext 和 GenericApplicationContext, // 因當(dāng)前是 Servlet Web 應(yīng)用,所以執(zhí)行的是 GenericApplicationContext 中的 refreshBeanFactory 方法。 // 該方法主要設(shè)置 BeanFactory 的 serializationId 屬性值,也就是序列化id refreshBeanFactory(); // 通過(guò) getBeanFactory 返回 BeanFactory 對(duì)象。同樣也是由子類擴(kuò)展,調(diào)用的是 GenericApplicationContext 類中的 getBeanFactory 方法。 // 返回的是 DefaultListableBeanFactory 。 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; } ... }
obtainFreshBeanFactory 方法很簡(jiǎn)單,但如果當(dāng)前是非 Servlet Web 應(yīng)用,執(zhí)行的就是 AbstractRefreshableApplicationContext 中的 refreshBeanFactory 方法,那可就復(fù)雜多了,這里就不展開(kāi)討論。之后,該方法還返回了 BeanFactory 對(duì)象,從這也可以看出 ApplicationContext 底層是以 BeanFactory 為基礎(chǔ),逐步擴(kuò)展 Spring 容器功能。
第三步:prepareBeanFactory
接著進(jìn)入 refresh 中的 prepareBeanFactory 方法。prepareBeanFactory 方法主要是對(duì) BeanFactory 做一些配置,包含各種類加載器、需要忽略的依賴以及后置處理器、解析器等,
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { ... prepareBeanFactory(beanFactory); ... } ... } protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 設(shè)置類加載器 beanFactory.setBeanClassLoader(getClassLoader()); // 設(shè)置表達(dá)式解析器,主要用來(lái)解析 EL 表達(dá)式; Bean 初始化完成后填充屬性時(shí)會(huì)用到 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); // 設(shè)置屬性注冊(cè)解析器,主要用來(lái)解析 Bean 中的各種屬性類型,如 String、int 等 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // 添加一個(gè)后置處理器:ApplicationContextAwareProcessor。 // 該后置處理器用于向?qū)崿F(xiàn)了 Aware 系列接口的 bean 設(shè)置相應(yīng)屬性。 // (后置處理器和 Aware 接口也是比較核心的概念,后面會(huì)有文章詳細(xì)討論) beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); // 以下接口,在自動(dòng)注入時(shí)會(huì)被忽略,其都是 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 需自動(dòng)注入時(shí),指定其注入的類型 。 // 如:注入 BeanFactory 時(shí),注入的類型對(duì)象為 ConfigurableListableBeanFactory 。 beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerResolvableDependency(ApplicationContext.class, this); // 添加 ApplicationListenerDetector 后置處理器。 // 該后置處理器用來(lái)檢測(cè)那些實(shí)現(xiàn)了 ApplicationListener 接口的 bean,并將其添加到應(yīng)用上下文的事件廣播器上。 beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // 判斷容器中是否存在 loadTimeWeaver Bean,如果存在則上下文使用臨時(shí)的 ClassLoader 進(jìn)行類型匹配。 // 集成 AspectJ 時(shí)會(huì)用到 loadTimeWeaver 對(duì)象。 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } // 注冊(cè)和環(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 方法中,主要對(duì) BeanFactory 添加了一系列屬性項(xiàng),如添加忽略自動(dòng)注入的接口、添加 BeanPostProcessor 后置處理器、手動(dòng)注冊(cè)部分特殊的 Bean及環(huán)境相關(guān)的 Bean 。
第四步:postProcessBeanFactory
postProcessBeanFactory 方法是上下文準(zhǔn)備的最后一步,主要用來(lái)注冊(cè) Web 請(qǐng)求相關(guān)的處理器、Bean及配置。
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { ... postProcessBeanFactory(beanFactory); ... } }
該方法也是由子類進(jìn)行擴(kuò)展,實(shí)現(xiàn)該方法的子類有:
前面也說(shuō)過(guò),當(dāng)前是 Servlet Web 應(yīng)用,所以創(chuàng)建的 ApplicationContext 上下文是 AnnotationConfigServletWebServerApplicationContext,執(zhí)行該類的 postProcessBeanFactory 方法。
public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { private final AnnotatedBeanDefinitionReader reader; private final ClassPathBeanDefinitionScanner scanner; private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>(); private String[] basePackages; ... @Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 先執(zhí)行父類 ServletWebServerApplicationContext 的 postProcessBeanFactory 方法。 // 跳轉(zhuǎn)到 1 查看父類實(shí)現(xiàn) super.postProcessBeanFactory(beanFactory); // basePackages 存儲(chǔ)的是類路徑。先判斷是否為 null,不為 null 則通過(guò) ClassPathBeanDefinitionScanner 的 scan 方法 // 掃描該路徑下符合條件的 Class,并將 Class 信息包裝成 BeanDefinition 注冊(cè)到容器中, // 當(dāng)然,這里沒(méi)有指定掃描路徑,所以不會(huì)進(jìn)入這個(gè) if。 // (BeanDefinition 概念會(huì)在后面章節(jié)詳細(xì)討論) if (this.basePackages != null && this.basePackages.length > 0) { this.scanner.scan(this.basePackages); } // annotatedClasses 存儲(chǔ)的 Class 集合。先判斷該集合是否為空,不為空則通過(guò) // AnnotatedBeanDefinitionReader 的 register 方法將 Class 信息包裝成 BeanDefinition 注冊(cè)到容器中, // 這里同樣沒(méi)有設(shè)置 Class 集合內(nèi)容,所以不會(huì)進(jìn)入這個(gè) if。 if (!this.annotatedClasses.isEmpty()) { this.reader.register(ClassUtils.toClassArray(this.annotatedClasses)); } } } 1、 public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext { ... @Override protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 添加 BeanPostProcessor 后置處理器:WebApplicationContextServletContextAwareProcessor, // 該后置處理器主要是從 ConfigurableWebApplicationContext 上下文中獲取 ServletContext 和 ServletConfig 對(duì)象 beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this)); // 添加一個(gè) 忽略自動(dòng)注入的接口 beanFactory.ignoreDependencyInterface(ServletContextAware.class); } ... }
postProcessBeanFactory 方法執(zhí)行的操作和前面類似,也是添加了后置處理器和忽略自動(dòng)注入的接口。
總結(jié)
ApplicationContext 上下文準(zhǔn)備工作基本結(jié)束,主要還是在 BeanFactory 中添加一系列后置處理器、注冊(cè)特殊的 Bean 及設(shè)置忽略自動(dòng)注入的接口。其中還提到了 Spring 容器的三個(gè)核心部分:Aware 系列接口、BeanPostProcessor 后置處理器、BeanDefinition ,這部分在后面的文章會(huì)逐步討論。接下來(lái)將對(duì) Spring 容器的核心功能展開(kāi)討論。
到此這篇關(guān)于Spring核心容器之ApplicationContext上下文啟動(dòng)準(zhǔn)備詳解的文章就介紹到這了,更多相關(guān)ApplicationContext上下文啟動(dòng)準(zhǔn)備內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot調(diào)整ApplicationContextAware如何實(shí)現(xiàn)類加載順序
- Spring容器-BeanFactory和ApplicationContext使用詳解
- SpringBoot ApplicationContextAware拓展接口使用詳解
- Spring中ApplicationContextAware的使用方法詳解
- Spring ApplicationContext接口功能詳細(xì)介紹
- springboot如何獲取applicationContext?servletContext
- Spring ApplicationContext上下文核心容器深入探究
- SpringBoot?容器刷新前回調(diào)ApplicationContextInitializer
- SpringBoot?ApplicationContext接口深入分析
相關(guān)文章
Springboot使用filter對(duì)response內(nèi)容進(jìn)行加密方式
這篇文章主要介紹了Springboot使用filter對(duì)response內(nèi)容進(jìn)行加密方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03用JAVA實(shí)現(xiàn)單鏈表,檢測(cè)字符串是否是回文串
這篇文章主要介紹了使用JAVA實(shí)現(xiàn)單鏈表,檢測(cè)字符串是否是回文串,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-11-11java實(shí)現(xiàn)浮點(diǎn)數(shù)轉(zhuǎn)人民幣的小例子
java實(shí)現(xiàn)浮點(diǎn)數(shù)轉(zhuǎn)人民幣的小例子,需要的朋友可以參考一下2013-03-03解讀Java和JavaScript區(qū)別與聯(lián)系
這篇文章主要介紹了解讀Java和JavaScript區(qū)別與聯(lián)系,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02Redis緩存,泛型集合與json字符串的相互轉(zhuǎn)換實(shí)例
這篇文章主要介紹了Redis緩存,泛型集合與json字符串的相互轉(zhuǎn)換實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07Java微信公眾平臺(tái)開(kāi)發(fā)(15) 微信JSSDK的使用
這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺(tái)開(kāi)發(fā)第十五步,微信JSSDK的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04Java中的關(guān)鍵字_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
關(guān)鍵字也稱為保留字,是指Java語(yǔ)言中規(guī)定了特定含義的標(biāo)示符。對(duì)于保留字,用戶只能按照系統(tǒng)規(guī)定的方式使用,不能自行定義2017-04-04