1. 程式人生 > >SpringBoot系列之外部Servlet容器啟動原理

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主類,啟動容器等