近一两年,SpringBoot减少了很多繁琐的Spring配置和基于Boot的SpringCloud的推广,越来越多的应用开始使用SpringBoot进行开发。SpringBoot以标准Java应用程序的形式启动一个web服务,将容器的存在隐藏在一个配置文件中,使用起来非常方便。Tomcat是SpringBoot的内置容器之一。本期我们就来看看SpringBoot是如何集成Tomcat来提供服务的。上一篇文章写了Tomcat的Digester组件解析配置文件server.xml,根据配置信息生成Tomcat实例。在SpringBoot中,实现基本类似。不同的是,大部分配置信息是默认的,其他用户特定的设置被读出、解析并设置到Tomcat的每个组件中,如application.properties等引导配置文件中。另一个区别是SpringBoot使用嵌入式Tomcat。当然以上两点是整个Boot项目中使用Tomcat的基本原则,但是在Boot中使用EmbeddedTomcat与使用Maven插件还是有一些区别的。这是Boot使用的三个嵌入式容器,Tomcat是默认启动的。要分析这个问题,我们应该从哪里入手呢?Boot启动的时候,很清楚的告诉我们这样一条信息s.b.c.e.t.TomcatEmbeddedServletContainer:Tomcatinitializedwithport这是logback输出的信息。前面部分是缩写形式写的包名,最重要的是Container,跳到类里看看。这行log能匹配到的是容器的init方法,类前面的一些细节内容。我们不要太在意它。必须是this.tomcat.start();这里的tomcat是EmbeddedTomcat类的一个实例。.这里的start操作是容器启动方法中的getServer和getConnector。熟悉Tomcat的朋友都知道,Tomcat中主要有以下几个组件:EgineHostContextWrapperConnector前四个从上到下是containerComponent,是一种包容关系。但是光靠这些还不足以让我们访问部署好的应用。这时候连接容器与外界的组件Connector就必不可少了。而且,当真正走到启动这一步时,容器的组件配置已经完成,只需要启动它提供服务即可。这些配置的读取在初始阶段之前已经完成。下图显示了在初始化阶段读取配置时的一些代码。没有特别的地方,设置BaseDir,解析配置,设置各个组件。另外,在SpringBoot应用启动的时候,会有这样几条日志输出。我们知道SpringMVC是通过DispatcherServlet来分发和处理请求的。在SpringBoot出现之前,需要在web.xml中进行配置,实现请求拦截。Servlet3.0之后,规则中加入了DynamicServlet和DynamicFilter的概念,组件可以在运行时动态注册到Context中。所以我们观察到的Context只是一个空的应用,然后动态的往里面添加Servlet,Filter等内容。除了直接以Jar形式执行Main方法外,SpringBoot还支持将Boot应用程序打包成War文件,部署在standard和container中,而无需使用Embedded容器。与执行Main方法启动SpringBoot应用相比,作为Web应用提供的Boot能力如何提供?看下图,Jar文件的META-INF中的services包含了一个SCI声明。这就是SpringBoot在标准Web容器中的有效性的秘密。SCI是做什么的?容器启动时会依次处理每个ServletContainerInitializer的HandlesTypes注解,然后调用所有ServletContainerInitializer对象的onStartup方法,将处理HandlesTypes注解得到的类数组传递给ServletContainerInitializer的onStartup方法。在configure阶段,我们将Boot打包到war提供的Initalizer中运行。这时候处理dispatcherServlet和用Main方法开始执行没什么区别。因此,当我们看到Boot应用程序只需要这么少的配置就可以像Web应用程序一样轻松执行的时候,我们一定要清醒地认识到,其背后的Embedded容器还是做了很多工作的,而且还涉及到各种新的J2EE规范.最重要的是,不管怎么变,本质上还是一样的。它用作标准上下文。唯一的区别是它是通过解析静态文件配置还是动态添加。【本文为专栏作家“侯书城”原创稿件,转载请通过作者微信公众号“Tomcat物语”获得授权】点此查看本作者更多好文
