tomcat作为springboot嵌入服务器时,是单独为当前springboot提供服务的,所以有很多地方可以简化:
- 支持项目
独立服务器:多项目、多Servlet
嵌入式:一般单项目(嵌入的项目)、单Servlet(DispatchServlet) - server构建
独立服务器:Catalina通过解析server.xml构建
嵌入式:源码构建 - 启动方式
独立服务器:Bootstrap引导启动
嵌入式:Tomcat类启动
对比了两种启动方式的差别,下面重点来看下springboot中是如何简化创建server并注入DispatchServlet的。
首先看springboot的启动入口:
SpringApplication.run:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
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);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
主要是创建spring容器ApplicationContext,并进行刷新,在servlet模式下实现类为AnnotationConfigServletWebServerApplicationContext,复写了onRefresh方法,在容器刷新时,进行webServer的创建工作。
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
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();
}
默认配置下,ServletWebServerFactory实现为TomcatServletWebServerFactory,采用工厂模式来创建Tomcat服务器:
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
这里我们可以发现,和独立服务器不一样,并没有创建Bootstrap、Catalina类,也没有通过server.xml配置文件来创建server,而是直接new了一个Tomcat,配置上connector、Engine、Host、Context等组件,在创建TomcatWebServer时启动tomcat:
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
}
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
...省略其他代码
this.tomcat.start();
...省略其他代码
创建完成的服务器包含一个service,一个connector, 一个engine,一个host和一个context,tomcat.start时调用了server.start(),剩下的就和独立部署时一样,启动这些connector,container了。
tomcat启动的流程就结束了,但是还有一个问题,这个过程中并没有看到tomcat加载Servlet,也没有解析web.xml,spring是通过DispatchServlet来处理请求的,它是怎么加载到容器中的呢?
回到spring创建webServer的地方:
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());
可以看到它传入了一个ServletContextInitializer,这个initializer经过一系列封装最终传入了前面创建的唯一的Context里面,在Context启动的初始化阶段会进行回调,DispatchServlet的添加就在这个initializer里面,initializer的实现为:
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
它的功能是从spring容器中找出所有实现了ServletContextInitializer来进行调用,而springboot的自动装配机制注入了DispatcherServletRegistrationBean,它实现了ServletContextInitializer,在tomcat容器启动时向context中注册了DispatcherServlet,具体流程代码就不贴了。
Tomcat启动完成注入了DispatcherServlet后,DispatcherServlet即可处理请求了。