前言
Tomcat容器最主要作用就是监听网络请求,并将请求封装成request和response对象最终交给Servlet去执行,而我们spring项目中用来供servlet调用的业务代码的入口一般就是Spring管理的Controller。而Springboot整合tomcat的话,就需要提供一个Servlet的标准实现,并将这个Servlet注册到Tomcat容器里,并且在Servlet处理请求时,能调用Spring所管理的Controller进而使用Spring容器的所有资源。所以这里最主要的两个问题是:
1、Springboot是怎么创建和启动Tomcat的?
2、Springboot怎么将DispatcherServlet注册到tomcat容器中?
在分析问题前,先看下Tomcat的架构图,有助于后面看代码:
这里可以看出一个Tomcat就是一个Server,一个Server下会有多个Service,Service只负责封装多个Connector和一个Container(Service本身不是容器,可以看做只是用来包装Connector和Container的壳,不负责具体功能),而Container(也叫engine)下又有多个Host,每个Host下对应多个Context,Context下才是我们的Servlet,Tomcat为了使整个架构灵活,所以抽象出这么多层,每层之间都可以根据不同的维度产生一对多个配置。对应到Tomcat类:
再看下主要容器的类图,这里所有的容器都继承自LifecycleBase抽象类,这个类抽象了控制生命周期的方法,比如init、start、stop、destory方法,而负责处理请求的四个容器又都继承了ContainerBase抽象类,这个抽象类最主要是抽象了pipeline(可以理解为一个调用链,一个接下一个的处理)来处理请求的逻辑,所以请求到达这四个容器后,实际是通过pipeline来处理请求,类图如下:
一、Springboot是怎么创建和启动Tomcat的?
Springboot启动的主要方法是AbstractApplicationContext
类的refresh
方法,而springboot启动Tomcat容器的逻辑在ServletWebServerApplicationContext
类的onRefresh
方法里,这里从这个方法的代码开始debug:
@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) {
// 1.0
ServletWebServerFactory factory = getWebServerFactory();
// 2.0
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();
}
1.0源码分析
/**
* Returns the {@link ServletWebServerFactory} that should be used to create the
* embedded {@link WebServer}. By default this method searches for a suitable bean in
* the context itself.
* @return a {@link ServletWebServerFactory} (never {@code null})
*/
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
这部分就是获取web容器的工厂类,这个工厂类是一个接口,先看下这个ServletWebServerFactory接口的实现类图:
可以看到有tomcat、Jetty、Undertow三种,那么这里get出来的具体是哪种?这个逻辑就在org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
这个类里,这个类是springboot容器初始化时自动转载进容器的,springboot自动加载的原理在这里,在spring-boot-autoconfigure包的spring.factories里配置了所有需要自动装载进容器的类,如下图:
这个类的作用就是告诉spring容器如何加载容器,这里先看下ServletWebServerFactoryAutoConfigurat