SpringBoot配置外部Tomcat專案啟動流程原始碼分析(長文)
本篇博文是SpringBoot配置外部Tomcat並打war包姊妹篇。
【3】SpringBoot 使用外部Tomcat啟動原理
① 首先看Servlet3.0中的規範
- javax.servlet.ServletContainerInitializer(其是一個介面) 類是通過JAR服務API查詢的。對於每個應用程式,ServletContainerInitializer的一個例項是由容器在應用程式啟動時建立。
- 提供servletcontainerinitializer實現的框架必須將名為javax.servlet的檔案捆綁到jar檔案的META-INF/services目錄中。根據JAR服務API,找到指向ServletContainerInitializer的實現類。
- 除了ServletContainerInitializer 之外,還有一個註解–@HandlesTypes。ServletContainerInitializer 實現上的handlesTypes註解用於尋找感興趣的類–要麼是@HandlesTypes註解指定的類,要麼是其子類。
- 不管後設資料完成的設定如何,都將應用handlesTypes註解。
- ServletContainerInitializer例項的onStartup 方法將在應用程式啟動時且任何servlet偵聽器事件被激發之前被呼叫。
- ServletContainerInitializer 的onStartup 方法呼叫是伴隨著一組類的(
Set<Class<?>> webAppInitializerClasses
),這些類要麼是initializer的擴充套件類,要麼是新增了@HandlesTypes註解的類。將會依次呼叫webAppInitializerClasses例項的onStartup方法。
總結以下幾點:
1)伺服器啟動(web應用啟動)會建立當前web應用裡面每一個jar包裡面ServletContainerInitializer例項;
2)jar包的META-INF/services資料夾下,有一個名為javax.servlet.ServletContainerInitializer
的檔案,內容就是ServletContainerInitializer
的實現類的全類名;
如下圖所示:
3)還可以使用@HandlesTypes,在應用啟動的時候載入我們感興趣的類;
4)容器啟動過程中首先呼叫ServletContainerInitializer 例項的onStartup方法。
ServletContainerInitializer 介面如下:
public interface ServletContainerInitializer {
void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException;
}
② 步驟分析如下
第一步,Tomcat啟動
第二步,根據Servlet3.0規範,找到ServletContainerInitializer ,進行例項化
jar包路徑:
org\springframework\spring-web\4.3.14.RELEASE\
spring-web-4.3.14.RELEASE.jar!\METAINF\services\
javax.servlet.ServletContainerInitializer:
Spring的web模組裡面有這個檔案:
org.springframework.web.SpringServletContainerInitializer
第三步,建立例項
SpringServletContainerInitializer
將@HandlesTypes(WebApplicationInitializer.class)
標註的所有這個型別的類都傳入到onStartup方法的Set集合,為這些WebApplicationInitializer
型別的類建立例項並遍歷呼叫其onStartup方法。
SpringServletContainerInitializer 原始碼如下(呼叫其onStartup方法):
//感興趣的類為WebApplicationInitializer及其子類
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
//先呼叫onStartup方法,會傳入一系列webAppInitializerClasses
@Override
public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
//遍歷感興趣的類
for (Class<?> waiClass : webAppInitializerClasses) {
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
//判斷是不是介面,是不是抽象類,是不是該型別
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
//例項化每個initializer並新增到initializers中
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
return;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
//依次呼叫initializer的onStartup方法。
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
如上所示,在SpringServletContainerInitializer方法中又呼叫每一個initializer的onStartup方法。即先呼叫SpringServletContainerInitializer例項的onStartup方法,在onStartup()方法內部又遍歷每一個WebApplicationInitializer型別的例項,呼叫其onStartup()方法。
WebApplicationInitializer(Web應用初始化器)是什麼?
在Servlet 3.0+環境中提供的一個介面,以便程式設計式配置ServletContext而非傳統的xml配置。該介面的例項被SpringServletContainerInitializer自動檢測(@HandlesTypes(WebApplicationInitializer.class)這種方式)
。而SpringServletContainerInitializer是Servlet 3.0+容器自動引導的。通過WebApplicationInitializer,以往在xml中配置的DispatcherServlet、Filter等都可以通過程式碼注入。你可以不用直接實現WebApplicationInitializer,而選擇繼承AbstractDispatcherServletInitializer。
WebApplicationInitializer型別的類如下圖:
可以看到,將會建立我們的com.web.ServletInitializer(繼承自SpringBootServletInitializer)例項,並呼叫onStartup方法。
第四步:我們的SpringBootServletInitializer的例項(com.web.ServletInitializer)會被建立物件,並執行onStartup方法(com.web.ServletInitializer繼承自SpringBootServletInitializer,故而會呼叫SpringBootServletInitializer的onStartup方法)
SpringBootServletInitializer原始碼如下:
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Logger initialization is deferred in case an ordered
// LogServletContextInitializer is being used
this.logger = LogFactory.getLog(getClass());
//建立WebApplicationContext
WebApplicationContext rootAppContext = createRootApplicationContext(
servletContext);
if (rootAppContext != null) {
//如果根容器不為null,則新增監聽--注意這裡的ContextLoaderListener,
//contextInitialized方法為空,因為預設application context已經被初始化
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
@Override
public void contextInitialized(ServletContextEvent event) {
// no-op because the application context is already initialized
}
});
}
else {
this.logger.debug("No ContextLoaderListener registered, as "
+ "createRootApplicationContext() did not "
+ "return an application context");
}
}
可以看到做了兩件事:建立RootAppContext 和為容器新增監聽。
建立WebApplicationContext 原始碼如下:
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
//建立SpringApplicationBuilder --這一步很關鍵
SpringApplicationBuilder builder = createSpringApplicationBuilder();
//設定應用主啟動類--本文這裡為com.web.ServletInitializer
builder.main(getClass());
*/從servletContext中獲取servletContext.getAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)作為parent。第一次獲取肯定為null
*/
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(
//以將ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE重置為null
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
//註冊一個新的ParentContextApplicationContextInitializer--包含parent
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
//註冊ServletContextApplicationContextInitializer--包含servletContext
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
//設定applicationContextClass為AnnotationConfigServletWebServerApplicationContext
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
builder = configure(builder);
//新增監聽器
builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
//返回一個準備好的SpringApplication ,準備run-很關鍵
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(getClass()));
}
Assert.state(!application.getAllSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
// Ensure error pages are registered
if (this.registerErrorPageFilter) {
application.addPrimarySources(
Collections.singleton(ErrorPageFilterConfiguration.class));
}
//啟動應用
return run(application);
}
【4】createRootApplicationContext詳細流程原始碼分析
① createRootApplicationContext().createSpringApplicationBuilder()
跟蹤程式碼到:
public SpringApplicationBuilder(Class<?>... sources) {
this.application = createSpringApplication(sources);
}
此時的Sources為空,繼續跟蹤程式碼:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//web應用型別--Servlet
this.webApplicationType = deduceWebApplicationType();
獲取ApplicationContextInitializer,也是在這裡開始首次載入spring.factories檔案
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
這裡是第二次載入spring.factories檔案
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
ApplicationContextInitializer是spring元件spring-context元件中的一個介面,主要是spring ioc容器重新整理之前的一個回撥介面,用於處於自定義邏輯。
ApplicationContextInitializer(應用上下文初始化器)是什麼?
在ConfigurableApplicationContext-Spring IOC容器稱為“已經被重新整理”狀態前的一個回撥介面去初始化ConfigurableApplicationContext。通常用於需要對應用程式上下文進行某些程式設計初始化的Web應用程式中。例如,與ConfigurableApplicationContext#getEnvironment()
對比,註冊property sources或啟用配置檔案。另外ApplicationContextInitializer(和子類)相關處理器例項被鼓勵使用去檢測org.springframework.core.Ordered介面是否被實現或是否存在org.springframework.core.annotation.Order註解,如果存在,則在呼叫之前對例項進行相應排序。
spring.factories檔案中的實現類:
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer
# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
設定WebApplicationType
private WebApplicationType deduceWebApplicationType() {
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
這裡主要是通過判斷REACTIVE相關的位元組碼是否存在,如果不存在,則web環境即為SERVLET型別。這裡設定好web環境型別,在後面會根據型別初始化對應環境。
設定Initializer–ApplicationContextInitializer型別
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
//執行緒上下文類載入器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
這裡ClassLoader 獲取的是執行緒上下文類載入器,這裡使用的是Tomcat啟動:
Set<String> names
如下:
獲取了6個instance:
設定監聽–ApplicationListener型別
此時的type為ApplicationListener,Set<String> names
如下:
至此SpringApplicationBuilder建立完畢。
② 新增ServletContextApplicationContextInitializer
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
此時SpringApplication Initializers和Listener如下:
③ 設定application.setApplicationContextClass
④ builder = configure(builder);
此時呼叫我們的ServletInitializer的configure方法:
⑤ SpringApplication application = builder.build()建立應用
把我們的主類新增到application 中:
⑥ 將ErrorPageFilterConfiguration新增到Set<Class<?>> primarySources
接下來該run(application)了注意直到此時,我們讓沒有建立我們想要的容器,容器將會在run(application)中建立。
SpringApplication.run原始碼如下所示:
/**
run Spring application,建立並重新整理一個新的ApplicationContext
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
//簡單的秒錶,允許對許多工計時,顯示每個指定任務的總執行時間和執行時間。非執行緒安全
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
//異常報告集合
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//java.awt.headless是J2SE的一種模式用於在缺少螢幕、鍵盤或者滑鼠時的系統配置,很
//多監控工具如jconsole 需要將該值設定為true,系統變數預設為true
configureHeadlessProperty();
//第一步:獲取並啟動監聽器 SpringApplicationRunListener只有一個實現類EventPublishingRunListener,
//EventPublishingRunListener有一個SimpleApplicationEventMulticaster
//SimpleApplicationEventMulticaster有一個defaultRetriver
//defaultRetriver有個屬性為applicationListeners
//每一次listeners.XXX()方法呼叫,都將會廣播對應事件給applicationListeners監聽器處理
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();//run方法第一次被呼叫時,呼叫listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//第二步:構造容器環境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//設定需要忽略的bean
configureIgnoreBeanInfo(environment);
//列印banner
Banner printedBanner = printBanner(environment);
//第三步:建立容器
context = createApplicationContext();
//第四步:例項化SpringBootExceptionReporter.class,用來支援報告關於啟動的錯誤
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//第五步:準備容器
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//第六步:重新整理容器
refreshContext(context);
//第七步:重新整理容器後的擴充套件介面
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//容器已經被重新整理,但是CommandLineRunners和ApplicationRunners還沒有被呼叫
listeners.started(context);
//呼叫CommandLineRunner和ApplicationRunner的run方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//在run結束前,且呼叫CommandLineRunner和ApplicationRunner的run方法後,呼叫
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
【5】SpringApplication.run方法詳細分析-獲取並啟動監聽器
① 獲取監聽器getRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
跟進SpringApplication.getRunListeners方法(返回SpringApplicationRunListeners):
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
上面可以看到,args本身預設為空,但是在獲取監聽器的方法中,getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)
將當前物件作為引數,該方法用來獲取spring.factories對應的監聽器:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
//獲取類載入器 WebappClassLoader
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//根據類載入器,獲取SpringApplicationRunListener(type)相關的監聽器
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//建立factories
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
Set<String> names
如下:
整個 springBoot 框架中獲取factories的方式統一如下:
@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
// //裝載class檔案到記憶體
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass
.getDeclaredConstructor(parameterTypes);
//主要通過反射建立例項
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
上面通過反射獲取例項時會觸發EventPublishingRunListener的建構函式。如下圖所示將會把application的listener新增到SimpleApplicationEventMulticaster initialMulticaster
的ListenerRetriever defaultRetriever
的Set<ApplicationListener<?>> applicationListeners中:
重點來看一下addApplicationListener方法:
public void addApplicationListener(ApplicationListener<?> listener) {
synchronized (this.retrievalMutex) {
// Explicitly remove target for a proxy, if registered already,
// in order to avoid double invocations of the same listener.
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
上述方法定義在SimpleApplicationEventMulticaster
父類AbstractApplicationEventMulticaster
中。關鍵程式碼為this.defaultRetriever.applicationListeners.add(listener);
,這是一個內部類,用來儲存所有的監聽器。也就是在這一步,將spring.factories中的監聽器傳遞到SimpleApplicationEventMulticaster中。
繼承關係如下:
② listeners.starting()–SpringApplicationRunListener啟動–監聽器第一次處理事件
listeners.starting();,獲取的監聽器為EventPublishingRunListener,從名字可以看出是啟動事件釋出監聽器,主要用來發布啟動事件。
SpringApplicationRunListener
是run()方法的監聽器,其只有一個實現類EventPublishingRunListener
。SpringApplicationRunListeners
是SpringApplicationRunListener
的集合類。
也就是說將會呼叫EventPublishingRunListener的starting()
方法。
public void starting() {
//關鍵程式碼,這裡是建立application啟動事件`ApplicationStartingEvent`
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
EventPublishingRunListener這個是springBoot框架中最早執行的監聽器,在該監聽器執行started()方法時,會繼續釋出事件,也就是事件傳遞。這種實現主要還是基於spring的事件機制。
繼續跟進SimpleApplicationEventMulticaster,有個核心方法:
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
// //獲取執行緒池,如果為空則同步處理。這裡執行緒池為空,還未沒初始化。
Executor executor = getTaskExecutor();
if (executor != null) {
非同步傳送事件
executor.execute(() -> invokeListener(listener, event));
}
else {
// //同步傳送事件
invokeListener(listener, event);
}
}
}
ApplicationListener<E extends ApplicationEvent>
介面有個抽象方法onApplicationEvent(E event)
子類必須實現。該方法用來處理對應事件。
其中getApplicationListeners(event, type)主要有四種listener:
- LoggingApplicationListener(處理日誌)
- BackgroundPreinitializer
- DelegatingApplicationListener
- LiquibaseServiceLocatorApplicationListener
這是springBoot啟動過程中,第一處根據型別,執行監聽器的地方。根據釋出的事件型別從上述10種監聽器中選擇對應的監聽器進行事件釋出,當然如果繼承了 springCloud或者別的框架,就不止10個了。這裡選了一個 springBoot 的日誌監聽器來進行講解,核心程式碼如下:
@Override
public void onApplicationEvent(ApplicationEvent event) {
//在springboot啟動的時候
if (event instanceof ApplicationStartedEvent) {
onApplicationStartedEvent((ApplicationStartedEvent) event);
}
//springboot的Environment環境準備完成的時候
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
//在springboot容器的環境設定完成以後
else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
//容器關閉的時候
else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)
.getApplicationContext().getParent() == null) {
onContextClosedEvent();
}
//容器啟動失敗的時候
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
因為我們的事件型別為ApplicationEvent,所以會執行onApplicationStartedEvent((ApplicationStartedEvent) event);。springBoot會在執行過程中的不同階段,傳送各種事件,來執行對應監聽器的對應方法。
【6】SpringApplication.run方法詳細分析-準備環境
③ prepareEnvironment–環境構建
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
跟進去該方法:
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//獲取對應的ConfigurableEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置
configureEnvironment(environment, applicationArguments.getSourceArgs());
//釋出環境已準備事件,這是第二次釋出事件
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
來看一下getOrCreateEnvironment()方法,前面已經提到,environment已經被設定了servlet型別,所以這裡建立的是環境物件是StandardServletEnvironment。
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (this.webApplicationType == WebApplicationType.SERVLET) {
return new StandardServletEnvironment();
}
return new StandardEnvironment();
}
列舉類WebApplicationType是SpringBoot2新增的特性,主要針對spring5引入的reactive特性。列舉型別如下:
public enum WebApplicationType {
//不需要再web容器的環境下執行,普通專案
NONE,
//基於servlet的web專案
SERVLET,
//這個是spring5版本開始的新特性
REACTIVE
}
Environment介面提供了4種實現方式,StandardEnvironment、StandardServletEnvironment和MockEnvironment、StandardReactiveWebEnvironment
,分別代表普通程式、Web程式、測試程式的環境、響應式web環境,
配置環境程式碼如下:
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
在返回return new StandardServletEnvironment();物件的時候,會完成一系列初始化動作,主要就是將執行機器的系統變數和環境變數,加入到其父類AbstractEnvironment定義的物件MutablePropertySources中,MutablePropertySources物件中定義了一個屬性集合:
private final List<PropertySource<?>> propertySourceList;
public MutablePropertySources() {
this.propertySourceList = new CopyOnWriteArrayList();
this.logger = LogFactory.getLog(this.getClass());
}
執行到這裡,系統變數和環境變數已經被載入到配置檔案的集合中,接下來就行解析專案中的配置檔案。
關於CopyOnWriteArrayList可以參考博文淺談從fail-fast機制到CopyOnWriteArrayList使用
④ listeners.environmentPrepared(environment);–第二次釋出事件
來看一下listeners.environmentPrepared(environment);,上面已經提到了,這裡是第二次釋出事件。什麼事件呢?顧名思義,系統環境初始化完成的事件。
跟進方法:
繼續跟:
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
這裡將要廣播ApplicationEnvironmentPreparedEvent事件了
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
釋出事件的流程上面已經講過了,這裡不在贅述。來看一下根據事件型別獲取到的監聽器:
遍歷監聽器,呼叫不同監聽器對該事件的處理。
可以看到獲取到的監聽器和第一次釋出啟動事件獲取的監聽器有幾個是重複的,這也驗證了監聽器是可以多次獲取,根據事件型別來區分具體處理邏輯。上面介紹日誌監聽器的時候已經提到。
主要來看一下ConfigFileApplicationListener,該監聽器非常核心,主要用來處理專案配置。專案中的 properties 和yml檔案都是其內部類所載入。
首先方法執行入口:
呼叫onApplicationEnvironmentPreparedEvent方法:
private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(),
event.getSpringApplication());
}
首先還是會去讀spring.factories 檔案,List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
獲取的處理類有以下四種:
SystemEnvironmentPropertySourceEnvironmentPostProcessor(載入系統環境變數):
SpringApplicationJsonEnvironmentPostProcessor:
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
MutablePropertySources propertySources = environment.getPropertySources();
StreamSupport.stream(propertySources.spliterator(), false)
.map(JsonPropertyValue::get).filter(Objects::nonNull).findFirst()
.ifPresent((v) -> processJson(environment, v));
}
在執行完上述三個監聽器流程後,ConfigFileApplicationListener會執行該類本身的邏輯。由其內部類Loader載入專案制定路徑下的配置檔案:
// Note the order is from least to most specific (last one wins)
private static final String DEFAULT_SEARCH_LOCATIONS =
"classpath:/,classpath:/config/,file:./,file:./config/";
ConfigFileApplicationListener
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
內部類Loader建構函式:
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.resourceLoader = (resourceLoader != null ? resourceLoader
: new DefaultResourceLoader());
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
PropertySourceLoader.class, getClass().getClassLoader());
}
其獲取的propertySourceLoaders 如下:
至此使用ConfigFileApplicationListener將應用配置檔案載入進來,接下來該其它六個監聽器依次處理。
監聽器處理完畢返回到ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments)
處,此時environment物件如下:
接下來將environment繫結到SpringApplication上。
返回到SpringApplication類中ConfigurableApplicationContext run(String... args)方法處
:
【7】SpringApplication.run方法詳細分析-建立容器
⑤ createApplicationContext();建立容器
在SpringBootServletInitializer.createRootApplicationContext(ServletContext servletContext)
中設定過contextClass:
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
看建立容器的程式碼:
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
//如果contextClass 為null,就根據webApplicationType載入對應class;
//這裡不為null,是AnnotationConfigServletWebServerApplicationContext
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_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);
}
建立的context如下:
⑥ 報告錯誤資訊
這裡還是以同樣的方式獲取 spring.factories檔案中的指定類:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
如果應用啟動失敗,則會列印失敗資訊:
@Override
public void report(FailureAnalysis failureAnalysis) {
if (logger.isDebugEnabled()) {
logger.debug("Application failed to start due to an exception",
failureAnalysis.getCause());
}
if (logger.isErrorEnabled()) {
logger.error(buildMessage(failureAnalysis));
}
}
【8】SpringApplication.run方法詳細分析-容器重新整理之前準備
⑦ prepareContext–準備容器
這一步主要是在容器重新整理之前的準備動作。包含一個非常關鍵的操作:將啟動類注入容器,為後續開啟自動化配置奠定基礎。
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//設定容器環境,包括各種變數
context.setEnvironment(environment);
//執行容器後置處理
postProcessApplicationContext(context);
//執行容器中的ApplicationContextInitializer(包括 spring.factories和自定義的例項)
applyInitializers(context);
//傳送容器已經準備好的事件,通知各監聽器
listeners.contextPrepared(context);
//列印log
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//註冊啟動引數bean,這裡將容器指定的引數封裝成bean,注入容器
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
//設定banner
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
//獲取我們的啟動類指定的引數,可以是多個
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//載入我們的啟動類,將啟動類注入容器
load(context, sources.toArray(new Object[0]));
//釋出容器已載入事件。
listeners.contextLoaded(context);
}
容器的後置處理:
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context)
.setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context)
.setClassLoader(this.resourceLoader.getClassLoader());
}
}
}
這裡預設不執行任何邏輯,因為beanNameGenerator和resourceLoader預設為空。之所以這樣做,是springBoot留給我們的擴充套件處理方式,類似於這樣的擴充套件,spring中也有很多。
初始化器初始化方法呼叫
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
上面提到過,有7個初始化器,這裡將會依次呼叫initialize方法:
ServletContextApplicationContextInitializer.initialize:
public void initialize(ConfigurableWebApplicationContext applicationContext) {
//給建立的容器設定servletContext引用
applicationContext.setServletContext(this.servletContext);
//判斷true or false ,如果為true,將applicationContext放到servletContext
//這裡為false。
if (this.addApplicationContextAttribute) {
this.servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
applicationContext);
}
}
ContextIdApplicationContextInitializer.initialize:
public void initialize(ConfigurableApplicationContext applicationContext) {
ContextId contextId = getContextId(applicationContext);
applicationContext.setId(contextId.getId());
applicationContext.getBeanFactory().registerSingleton(ContextId.class.getName(),
contextId);
}
為applicationContext設定ID。
listeners.contextPrepared(context)釋出事件–第三次釋出事件了
public void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
這裡listener為EventPublishingRunListener,其contextPrepared方法為空方法:
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
}
獲取primarySources放到allSources
可以看到我們的主啟動類在裡面。
載入啟動指定類(重點)
這裡會將我們的啟動類載入spring容器beanDefinitionMap中,為後續springBoot 自動化配置奠定基礎,springBoot為我們提供的各種註解配置也與此有關。
這裡引數即為我們專案啟動時傳遞的引數:SpringApplication.run(SpringbootwebprojectApplication.class, args);
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug(
"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
//根據BeanDefinitionRegistry建立BeanDefinitionLoader
BeanDefinitionLoader loader = createBeanDefinitionLoader(
getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
//設定環境
loader.setEnvironment(this.environment);
}
loader.load();
}
因為這裡的applicationContext實現了BeanDefinitionRegistry介面,所以獲取BeanDefinitionRegistry時將applicationContext強轉返回。故而這裡直接拿著applicationContext和sources獲取BeanDefinitionLoader
需要注意的是,springBoot2會優先選擇groovy載入方式,找不到再選用java方式。或許groovy動態載入class檔案的效能更勝一籌。
private int load(Class<?> source) {
if (isGroovyPresent()
&& GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
GroovyBeanDefinitionSource.class);
load(loader);
}
if (isComponent(source)) {
//以註解的方式,將啟動類bean資訊存入beanDefinitionMap
this.annotatedReader.register(source);
return 1;
}
return 0;
}
listeners.contextLoaded(context);廣播事件容器已準備就緒–監聽器第四次處理事件
public void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextLoaded(context);
}
}
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
//將application.getListeners()新增到context
context.addApplicationListener(listener);
}
//廣播事件給監聽器
this.initialMulticaster.multicastEvent(
new ApplicationPreparedEvent(this.application, this.args, context));
}
此時是有四個監聽器處理ApplicationPreparedEvent事件:
ConfigFileApplicationListener.onApplicationPreparedEvent:
private void onApplicationPreparedEvent(ApplicationEvent event) {
this.logger.replayTo(ConfigFileApplicationListener.class);
addPostProcessors(((ApplicationPreparedEvent) event).getApplicationContext());
}
addPostProcessors原始碼如下:
protected void addPostProcessors(ConfigurableApplicationContext context) {
//給context中新增BeanFactoryPostProcessor
context.addBeanFactoryPostProcessor(
new PropertySourceOrderingPostProcessor(context));
}
【9】SpringApplication.run方法詳細分析-重新整理容器
refreshContext(context);重新整理容器,準備IOC容器的核心步驟就在這裡。
AbstractApplicationContext.refresh方法原始碼如下:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
/**
* 重新整理上下文環境
* 初始化上下文環境,對系統的環境變數或者系統屬性進行準備和校驗
* 如環境變數中必須設定某個值才能執行,否則不能執行,這個時候可以在這裡加這個校驗,
* 重寫initPropertySources方法就好了
*/
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
/**
* 初始化BeanFactory,解析XML,相當於之前的XmlBeanFactory的操作,
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
/**
* 為上下文準備BeanFactory,即對BeanFactory的各種功能進行填充,如常用的註解@Autowired @Qualifier等
* 設定SPEL表示式#{key}的解析器
* 設定資源編輯註冊器,如PerpertyEditorSupper的支援
* 新增ApplicationContextAwareProcessor處理器
* 在依賴注入忽略實現*Aware的介面,如EnvironmentAware、ApplicationEventPublisherAware等
* 註冊依賴,如一個bean的屬性中含有ApplicationEventPublisher(beanFactory),則會將beanFactory的例項注入進去
*/
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
/**
* 提供子類覆蓋的額外處理,即子類處理自定義的BeanFactoryPostProcess
*/
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
/**
* 啟用各種BeanFactory處理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
* 執行對應的postProcessBeanDefinitionRegistry方法 和 postProcessBeanFactory方法
*/
invokeBeanFactoryPostProcessors(beanFactory);
/**
* 註冊攔截Bean建立的Bean處理器,即註冊BeanPostProcessor,不是BeanFactoryPostProcessor,注意兩者的區別
* 注意,這裡僅僅是註冊,並不會執行對應的方法,將在bean的例項化時執行對應的方法
*/
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
/**
* 初始化上下文中的資原始檔,如國際化檔案的處理等
*/
initMessageSource();
// Initialize event multicaster for this context.
/**
* 初始化上下文事件廣播器,並放入applicatioEventMulticaster,如ApplicationEventPublisher
*/
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
/**
* 給子類擴充套件初始化其他Bean
* 在沒有使用外部Tomcat專案中,還會在這裡建立內建Tomcat WebServer 並啟動
*/
onRefresh();
// Check for listener beans and register them.
/**
* 在所有bean中查詢listener bean,然後註冊到廣播器中
*/
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
/**
* 設定轉換器
* 註冊一個預設的屬性值解析器
* 凍結所有的bean定義,說明註冊的bean定義將不能被修改或進一步的處理
* 初始化剩餘的非惰性的bean,即初始化非延遲載入的bean
*/
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
/**
* 初始化生命週期處理器DefaultLifecycleProcessor,DefaultLifecycleProcessor含有start方法和stop方法,spring啟動的時候呼叫start方法開始生命週期,
* spring關閉的時候呼叫stop方法來結束生命週期,通常用來配置後臺程式,啟動有一直執行,如一直輪詢kafka
* 啟動所有實現了Lifecycle介面的類
* 通過spring的事件釋出機制釋出ContextRefreshedEvent事件,以保證對應的監聽器做進一步的處理,即對那種在spring啟動後需要處理的一些類,這些類實現了
* ApplicationListener<ContextRefreshedEvent> ,這裡就是要觸發這些類的執行(執行onApplicationEvent方法)另外,spring的內建Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
* 完成初始化,通知生命週期處理器lifeCycleProcessor重新整理過程,同時發出ContextRefreshEvent通知其他人
*/
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();
}
}
}
① obtainFreshBeanFactory();
預設獲取的BeanFactory為DefaultListableBeanFactory,原始碼如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
這裡獲取後的beanFactory如下所示:
②prepareBeanFactory-準備BeanFactory
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
//設定BeanClassLoader-這裡是WebappClassLoader
beanFactory.setBeanClassLoader(getClassLoader());
//設定SPEL表示式#{key}的解析器 --StandardBeanExpressionResolver
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
//設定資源編輯註冊器,如PerpertyEditorSupper的支援-- ResourceEditorRegistrar
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
//新增ApplicationContextAwareProcessor處理器
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
//在依賴注入忽略實現*Aware的介面,
//如EnvironmentAware、ApplicationEventPublisherAware等
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
註冊依賴,如一個bean的屬性中含有ApplicationEventPublisher(beanFactory),
//則會將beanFactory的例項注入進去
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
將早期後處理器註冊為應用程式監聽器,以檢測內部bean。
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
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());
}
}
其父類ServletWebServerApplicationContext.postProcessBeanFactory如下:
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//新增WebApplicationContextServletContextAwareProcessor後置處理器
beanFactory.addBeanPostProcessor(
new WebApplicationContextServletContextAwareProcessor(this));
//忽略依賴介面
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
}
③ 後置處理BeanFactorypostProcessBeanFactory(beanFactory);
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.postProcessBeanFactory(beanFactory);
if (this.basePackages != null && this.basePackages.length > 0) {
this.scanner.scan(this.basePackages);
}
if (!this.annotatedClasses.isEmpty()) {
this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
}
註冊完了,該呼叫BeanFactoryPostProcessors了。
④ invokeBeanFactoryPostProcessors(beanFactory)
在單例例項化前呼叫所有註冊的BeanFactoryPostProcessor:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
這裡有三個BeanFactoryPostProcessor:
- ConfigurationWarningsApplicationContextInitializer.ConfigurationWarningsPostProcessor(後者是前者static final內部類);
- SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPostProcessor(後者是前者靜態內部類);
- ConfigFileApplicationListener.PropertySourceOrderingPostProcessor(後者是前者內部類);
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors:
class PostProcessorRegistrationDelegate {
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
if (beanFactory instanceof BeanDefinitionRegistry) {
//如果beanFactory是BeanDefinitionRegistry型別,就強轉一下
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new LinkedList<>();
//遍歷beanFactoryPostProcessors
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
//如果postProcessor 還是BeanDefinitionRegistryPostProcessor型別,就強轉一下
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
//呼叫registryProcessor 的postProcessBeanDefinitionRegistry方法
registryProcessor.postProcessBeanDefinitionRegistry(registry);
//新增到registryProcessors
registryProcessors.add(registryProcessor);
}else {
//否則新增到List<BeanFactoryPostProcessor> regularPostProcessors
//也就是說只是BeanFactoryPostProcessor
regularPostProcessors.add(postProcessor);
}
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
}
else {
// Invoke factory processors registered with the context instance.
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
}
else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
// Finally, invoke all other BeanFactoryPostProcessors.
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
// Clear cached merged bean definitions since the post-processors might have
// modified the original metadata, e.g. replacing placeholders in values...
beanFactory.clearMetadataCache();
}
遍歷BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors
後List<BeanFactoryPostProcessor> regularPostProcessors
和List<BeanDefinitionRegistryPostProcessor> registryProcessors
如下所示:
⑤ registerBeanPostProcessors(beanFactory);
例項化並呼叫所有已經註冊的BeanPostProcessor:
/**
* Instantiate and invoke all registered BeanPostProcessor beans,
* respecting explicit order if given.
* <p>Must be called before any instantiation of application beans.
*/
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
⑥ initMessageSource();
初始化上下文中的資原始檔,如國際化檔案的處理等:
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// Make MessageSource aware of parent MessageSource.
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
// Only set parent context as parent MessageSource if no parent MessageSource
// registered already.
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using MessageSource [" + this.messageSource + "]");
}
}
else {
// Use empty MessageSource to be able to accept getMessage calls.
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isTraceEnabled()) {
logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
}
}
}
⑦ initApplicationEventMulticaster
初始化上下文事件廣播器,如果不存在ApplicationEventMulticaster 則使用預設SimpleApplicationEventMulticaster。
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
注意,在這之前,事情釋出機制都是通過SpringApplicationRunListener的實現類EventPublishingRunListener來完成的,後者內部有一個ApplicationEventMulticaster進行事件釋出給ApplicationListener。在這個方法中,直接使用this.applicationEventMulticaster=
賦值了一個應用事件廣播器。
⑧ onRefresh()
ServletWebServerApplicationContext.onRefresh()方法原始碼如下:
protected void onRefresh() {
//呼叫父類onRefresh()方法
super.onRefresh();
try {
//建立WebServer
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
呼叫父類onRefresh()方法:
/**
* Initialize the theme capability.
*/
@Override
protected void onRefresh() {
this.themeSource = UiApplicationContextUtils.initThemeSource(this);
}
/**
* Initialize the ThemeSource for the given application context,
* autodetecting a bean with the name "themeSource". If no such
* bean is found, a default (empty) ThemeSource will be used.
* @param context current application context
* @return the initialized theme source (will never be {@code null})
* @see #THEME_SOURCE_BEAN_NAME
*/
public static ThemeSource initThemeSource(ApplicationContext context) {
if (context.containsLocalBean(THEME_SOURCE_BEAN_NAME)) {
ThemeSource themeSource = context.getBean(THEME_SOURCE_BEAN_NAME, ThemeSource.class);
// Make ThemeSource aware of parent ThemeSource.
if (context.getParent() instanceof ThemeSource && themeSource instanceof HierarchicalThemeSource) {
HierarchicalThemeSource hts = (HierarchicalThemeSource) themeSource;
if (hts.getParentThemeSource() == null) {
// Only set parent context as parent ThemeSource if no parent ThemeSource
// registered already.
hts.setParentThemeSource((ThemeSource) context.getParent());
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using ThemeSource [" + themeSource + "]");
}
return themeSource;
}
else {
// Use default ThemeSource to be able to accept getTheme calls, either
// delegating to parent context's default or to local ResourceBundleThemeSource.
HierarchicalThemeSource themeSource = null;
if (context.getParent() instanceof ThemeSource) {
themeSource = new DelegatingThemeSource();
themeSource.setParentThemeSource((ThemeSource) context.getParent());
}
else {
themeSource = new ResourceBundleThemeSource();
}
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ThemeSource with name '" + THEME_SOURCE_BEAN_NAME +
"': using default [" + themeSource + "]");
}
return themeSource;
}
}
ServletWebServerApplicationContext.createWebServer()方法如下:
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
判斷ServletContext不為null,就會呼叫getSelfInitializer().onStartup(servletContext);
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
beanFactory);
WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
getServletContext());
existingScopes.restore();
WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
getServletContext());
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
如下圖所示,在末尾迴圈遍歷ServletContextInitializerBean,這裡有6個,分別註冊了Servlet和Filter。
⑨ registerListeners();
新增實現了ApplicationListener 介面的bean作為監聽器,並不影響其他監聽器。
/**
* Add beans that implement ApplicationListener as listeners.
* Doesn't affect other listeners, which can be added without being beans.
*/
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
⑩ finishBeanFactoryInitialization
- 設定轉換器
- 註冊一個預設的屬性值解析器
- 凍結所有的bean定義,說明註冊的bean定義將不能被修改或進一步的處理
- 初始化剩餘的非惰性的bean,即初始化非延遲載入的bean
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
(11)finishRefresh()
完成上下文重新整理,呼叫LifecycleProcessor的onRefresh()方法併發布ContextRefreshedEvent事件。
/**
* Finish the refresh of this context, invoking the LifecycleProcessor's
* onRefresh() method and publishing the
* {@link org.springframework.context.event.ContextRefreshedEvent}.
*/
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
【10】SpringApplication.run方法詳細分析-afterRefresh
參考博文:
SpringBoot、SpringMVC 和SpringMVC核心容器建立及DispatcherServlet執行原理
相關文章
- Tomcat原始碼分析--啟動流程Tomcat原始碼
- 配置springboot專案使用外部tomcatSpring BootTomcat
- SpringBoot原始碼學習4——SpringBoot內嵌Tomcat啟動流程原始碼分析Spring Boot原始碼Tomcat
- Springboot2專案配置(熱部署+war+外部tomcat+外部配置檔案)Spring Boot熱部署Tomcat
- SpringBoot2 | SpringBoot啟動流程原始碼分析(一)Spring Boot原始碼
- Flutter啟動流程原始碼分析Flutter原始碼
- apiserver原始碼分析——啟動流程APIServer原始碼
- Activity啟動流程原始碼分析原始碼
- TOMCAT原始碼分析(啟動框架)Tomcat原始碼框架
- springboot 專案部署到外部tomcatSpring BootTomcat
- Springboot專案部署到外部TomcatSpring BootTomcat
- jetty啟動web專案原始碼分析JettyWeb原始碼
- SpringBoot一站式啟動流程原始碼分析Spring Boot原始碼
- SpringBoot原始碼解析-啟動流程(二)Spring Boot原始碼
- SpringBoot原始碼解析-啟動流程(一)Spring Boot原始碼
- JobTracker啟動流程原始碼級分析原始碼
- Springboot整合外部Tomcat配置Spring BootTomcat
- SpringBoot 之配置外部TomcatSpring BootTomcat
- SpringBoot中配置外部TomcatSpring BootTomcat
- SpringBoot 使用外部Tomcat方法及啟動原理Spring BootTomcat
- Android Activity啟動流程原始碼分析Android原始碼
- Android原始碼分析:Activity啟動流程Android原始碼
- Netty啟動流程及原始碼分析Netty原始碼
- Egg.js 原始碼分析-專案啟動JS原始碼
- SpringBoot原始碼學習3——SpringBoot啟動流程Spring Boot原始碼
- 以太坊原始碼分析(39)geth啟動流程分析原始碼
- 使用springboot建立web專案,並使用外部TomcatSpring BootWebTomcat
- Apache Flink原始碼分析---JobManager啟動流程Apache原始碼
- spark core原始碼分析2 master啟動流程Spark原始碼AST
- spark core原始碼分析4 worker啟動流程Spark原始碼
- containerd 原始碼分析:啟動註冊流程AI原始碼
- Springboot 載入配置檔案原始碼分析Spring Boot原始碼
- springboot2.0使用外部tomcat進行啟動方法Spring BootTomcat
- SpringBoot原始碼解析-內嵌Tomcat容器的啟動Spring Boot原始碼Tomcat
- 詳解Tomcat系列(一)-從原始碼分析Tomcat的啟動Tomcat原始碼
- 【Spring原始碼分析】配置檔案讀取流程Spring原始碼
- SpringBoot啟動流程分析原理(一)Spring Boot
- Scrapy原始碼閱讀分析_2_啟動流程原始碼