前言
上下文Context可以說spring boot中最重要的一個概念,不僅包含了tomcat和spring mvc的啓動和管理,還對spring mvc原有模式中的bean註冊進行了大幅簡化,理解Spring boot的Context可以說是理解spring boot的基礎。
原理分析(六)介紹了spring boot啓動的主流程,run方法中最主要的部分即上下文的準備。
public ConfigurableApplicationContext run(String... args) {
......
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
......
return context;
}
上文代碼中包括context的創建、準備、刷新和刷新後處理。刷新後處理的方法afterRefresh是模板方法,供子類繼承填充代碼邏輯。
上下文創建
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
上下文創建createApplicationContext還是會依據SERVLET、REACTIVE和NONE類型來創建相應的上下文。類型解釋再引用一下原理分析(六)的內容。
SERVLET、REACTIVE和NONE。我們講spring mvc是Servlet容器,所以服務即SERVLET WEB類型。REACTIVE也是一種WEB服務的類型,代表着非阻塞響應式編程,正是spirng mvc不擅長的事,具體可以參考spring-webflux。NONE類型說這個服務不是WEB類型,是其他服務類型。
Servlet服務對應的環境上下文是AnnotationConfigServletWebServerApplicationContext類,Reactive服務對應的環境上下文是AnnotationConfigReactiveWebServerApplicationContext類,而默認的上下文AnnotationConfigApplicationContext類。
上下文Context的定義
根據ApplicationContext接口定義可以發現上下文的本質其實是一個“雜貨鋪”,繼承的父類顯示了上下文中包含的內容。
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
@Nullable
String getId();
String getApplicationName();
String getDisplayName();
long getStartupDate();
@Nullable
ApplicationContext getParent();
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
- 繼承的父類接口EnvironmentCapable賦予了上下文獲取應用程序的運行環境配置的能力。環境配置包括profile和properties配置,properties配置不僅包括項目內的properties文件,還有JVM system properties、操作系統環境變量等。
- ListableBeanFactory和HierarchicalBeanFactory賦予了上下文獲取bean、註冊bean的能力。
- MessageSource主要是國際化配置,比如語言等
- ApplicationEventPublisher提供了上下文發送上下文狀態改變事件的能力。這部分內容在原理分析(七)中有比較詳細的介紹。
- ResourcePatternResolver繼承了ResourceLoader,是用來做資源的格式解析,比如各種配置文件等等。
這裏需要注意的是,ApplicationContext接口中定義的都是getter類型的接口方法,沒有setter類型的接口方法。這是因爲不是所有上下文對象都提供修改內容的接口方法,只有ConfigurableApplicationContext接口的實現類提供了setter方法,可以修改上下文中的配置。這樣做是爲了防止上下文內容被隨意變更。
上下文Context的實現
SERVLET上下文的最終實現是AnnotationConfigServletWebServerApplicationContext類,上圖展示了這個類的繼承結構。 在ApplicationContext的更上層是基礎層的接口,上面已經介紹了,這些接口打包構成了上下文的“雜貨鋪”。ApplicationContext下層是其不同類型的子接口或者子類,主要是根據功能或者應用對象對上下文進行了分類。
WebApplicationContext接口添加了ServletContext getServletContext()方法。ServletContext的相關內容在spring mvc的原理(七)中,ServletConfig能夠獲取ServletContext的配置,其實代表就是spring mvc的應用本身。
WebServerApplicationContext的接口裏增加了WebServer getWebServer()的方法,這裏具體代表的就是tomcat服務器本身。
ConfigurableApplicationContext上面已經介紹過,是一個功能性的子接口,爲ApplicationContext提供了setter的方法,所有有繼承改接口的子類都擁有修改配置的能力。
所以這樣就很容易判斷ConfigurableWebApplicationContext和ConfigurableWebServerApplicationContext具有的能力,實際上是功能分類和應用對象分類的一個拼合。
接下來比較重要的是GenericApplicationContext,這個子類的特別之處在於繼承了bean定義的註冊BeanDefinitionRegistry。BeanDefinitionRegistry在原理分析(三)有過介紹,是用來用來註冊bean的。GenericApplicationContext的使用方法的官方解釋是,先使用BeanDefinitionRegistry註冊一系列的bean,然後調用 AbstractApplicationContext.refresh()初始化這些bean。與普通ApplicationContext的實現類不同的是,GenericApplicationContext不會每次refresh創建一個新的BeanFactory實例,而是在構造時就會獲取並持有一個BeanFactory實例,AbstractApplicationContext.refresh()可能只會被調用一次。
使用以上子類或者子接口的功能進行拼合,就得出了GenericWebApplicationContext和ServletWebServerApplicationContext這兩個類。
最後對於AnnotationConfigServletWebServerApplicationContext,除了上述所有子接口和子類的功能之外,還實現了AnnotationConfigRegistry接口。AnnotationConfigRegistry這個接口主要是負責bean註解的註冊和掃描工作,比如@Configuration註解。
附
本文主要介紹了Spring boot的ApplicationContext的定義實現和創建,後續會對上下文的準備和刷新進行介紹。