SpringBoot系列之外部Servlet容器啟動原理
外部Servlet容器啟動springboot應用原理
瞭解外部Servlet容器啟動springboot的原理,需要先了解一個規則,這個規則可以參考servlet3.0規範的8.2.4章 Shared libraries / runtimes pluggability:
本文摘錄幾條主要規則:
1)、伺服器啟動(web應用啟動)會建立當前web應用裡面每一個jar包裡面ServletContainerInitializer例項:
2)、ServletContainerInitializer的實現放在jar包的META-INF/services資料夾下,有一個名為
javax.servlet.ServletContainerInitializer的檔案,內容就是ServletContainerInitializer的實現類的全類名
3)、還可以使用@HandlesTypes,在應用啟動的時候載入我們感興趣的類;
這些規則有助於理解接下來討論的springboot流程:
1)、啟動Tomcat
2)、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
3)、SpringServletContainerInitializer將@HandlesTypes(WebApplicationInitializer.class)標註的所有這個型別
的類都傳入到onStartup方法的Set;為這些WebApplicationInitializer型別的類建立例項;
注:上一篇文章說的使用外部容器啟動需要編寫一個SpringBootServletInitializer的子類,通過繼承樹可以看到就是WebApplicationInitializer的子類
4)、每一個WebApplicationInitializer都呼叫自己的onStartup;
5)、相當於我們的SpringBootServletInitializer的類會被建立物件,並執行onStartup方法
6)、SpringBootServletInitializer例項執行onStartup的時候會createRootApplicationContext;建立容器
protected WebApplicationContext createRootApplicationContext( ServletContext servletContext) { // 建立SpringApplicationBuilder SpringApplicationBuilder builder = createSpringApplicationBuilder(); // 配置環境 StandardServletEnvironment environment = new StandardServletEnvironment(); environment.initPropertySources(servletContext, null); builder.environment(environment); // 設定主類 builder.main(getClass()); ApplicationContext parent = getExistingRootWebApplicationContext(servletContext); if (parent != null) { this.logger.info("Root context already created (using as parent)."); servletContext.setAttribute( WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null); builder.initializers(new ParentContextApplicationContextInitializer(parent)); } builder.initializers( new ServletContextApplicationContextInitializer(servletContext)); builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class); // 呼叫configure方法,子類重寫了這個方法,將SpringBoot的主程式類傳入了進來 builder = configure(builder); // 使用builder建立一個Spring應用 SpringApplication application = builder.build(); if (application.getSources().isEmpty() && AnnotationUtils .findAnnotation(getClass(), Configuration.class) != null) { application.getSources().add(getClass()); } Assert.state(!application.getSources().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.getSources().add(ErrorPageFilterConfiguration.class); } // 啟動Spring應用 return run(application); }
7)、Spring的應用就啟動並且建立IOC容器
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 重新整理IOC容器
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
} catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
綜上,整個過程就是啟動Servlet容器,再啟動SpringBoot應用
本文可簡述如下:
1.建立SpringBootServletInitializer的子類並重寫configure方法
2.configure方法中SpringApplicationBuilder傳入SpringBootApplication主程式類
– builder.source(@SpringBootApplication類)
3.啟動原理
– Servlet3.0標準ServletContainerInitializer掃描所有jar包中METAINF/services/javax.servlet.ServletContainerInitializer檔案指定的類並載入
– 載入spring web包下的SpringServletContainerInitializer
– 掃描@HandleType(WebApplicationInitializer)
– 載入SpringBootServletInitializer並執行onStartup方法
– 載入@SpringBootApplication主類,啟動容器等