使用SpringBoot开发,如果要打成war包,那么工程的启动类就必须实现WebApplicationInitializer
接口,如下(父类SpringBootServletInitializer
实现了该接口):
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(
SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
之所以需要实现该接口,是因为当SpringBoot不是使用内置Tomcat启动时,那么它就需要由外部的Tomcat启动,根据Servlet 3.0协议,Tomcat容器在启动的时候会扫描工程内所有实现ServletContainerInitializer
接口的类,根据这个原理,Spring提供了SpringServletContainerInitializer
// 注解@HandlesTypes表示当前需要Tomcat帮忙加载的接口实现类
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses,
ServletContext servletContext) throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList<WebApplicationInitializer>();
// 校验Tomcat启动过程传递进来的webAppInitializerClasses实现类;
// 收集到一个集合中
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 {
initializers.add((WebApplicationInitializer) waiClass.newInstance());
} catch (Throwable ex) {
// 忽略异常...
}
}
}
}
// ...
// 排序
AnnotationAwareOrderComparator.sort(initializers);
// 执行收集方法的onStartup方法,这里会收集到工程启动类
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
}
通过上面源码了解到通过servlet 3.0协议,Spring令Tomcat启动的时候回调SpringServletContainerInitializer
类,并将工程内所有符合条件的WebApplicationInitializer
实现类逐一启动
回到本文开头的工程启动类,它并没有#onStartup
方法的实现,那么必然在父类SpringBootServletInitializer
public abstract class SpringBootServletInitializer
implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// Logger initialization is deferred in case a ordered
// LogServletContextInitializer is being used
WebApplicationContext rootAppContext = createRootApplicationContext(
servletContext);
// ...
}
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
SpringApplicationBuilder builder = createSpringApplicationBuilder();
StandardServletEnvironment environment = new StandardServletEnvironment();
environment.initPropertySources(servletContext, null);
builder.environment(environment);
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigEmbeddedWebApplicationContext.class);
// 此处回调子类,即本文开头工程启动类
// 主要获取启动类
builder = configure(builder);
SpringApplication application = builder.build();
if (application.getSources().isEmpty()
&& AnnotationUtils.findAnnotation(getClass(), Configuration.class) != null) {
application.getSources().add(getClass());
}
// Ensure error pages are registered
if (this.registerErrorPageFilter) {
application.getSources().add(ErrorPageFilterConfiguration.class);
}
return run(application);
}
protected WebApplicationContext run(SpringApplication application) {
// 启动Spring容器
return (WebApplicationContext) application.run();
}
}
接下来便是熟悉的Spring容器启动逻辑
public class SpringApplication {
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);
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);
}
}
}
之后便是熟悉的Spring容器启动过程,可以参考前文,与之不同的是,由于当前由Tomcat驱动Spring容器启动,那么容器无须额外启动Tomcat(事实上,也不会将内置Tomcat一起打包)
public class ServletWebServerApplicationContext
extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
// servletContext 存在,不执行Tomcat的创建
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
} else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
} catch (ServletException ex) {
// ...
}
}
initPropertySources();
}
}