本文介绍SpringBoot内置web服务器。知识点有SpringBoot默认web服务器;如何配置当前web容器;内嵌Web服务器如何切换(从tomcat到jetty);Web容器怎么自动配置;web容器启动源码解析;SpringBoot内置服务器不使用SPI机制特别说明。
一、SpringBoot默认web服务器?
在SpringBoot中采用的默认web服务器是Tomcat,要了解为什么是Tomcat可从源码入手。
对于web服务器的配置,也是在自动配置中找,前面学习了SpringBoot自动配置WebMVC的知识,可以推测对于Web服务器的配置应该也是在一个自动配置类当中进行的,那么可以去/META-INF/spring.factories文件找一下WebMVC的自动配置,在这个自动配置内可以间接找到关于Web服务器的配置。
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
在上面SpringBoot包的目录找到这个路径下的Web服务器自动配置类。
这个Web服务器的自动配置类,我们可以看到这个配置类支持3种web服务器(Tomcat,Jetty,Undertow),具体要配置哪种服务器由ServletWebServerFactoryConfiguration来决定,同时这里还定义了一个顺序,依次是Tomcat->Jetty->Undertow。
那要选择哪种服务器呢?看ServletWebServerFactoryConfiguration。
在这个web服务器工厂配置类中,分别对上述三种服务器进行了定义:
对Tomcat定义:判断环境中是否引入了Tomcat所需的依赖Servlet.class, Tomcat.class, UpgradeProtocol.class,同时用户没有自己进行Web服务器配置(比如自己通过实现ServletWebServerFactory接口进行手动配置web服务器),那么这个Tomcat服务器就会生效。
对Jetty定义:所需要的依赖有Servlet.class, Server.class, Loader.class, WebAppContext.class
对Undertow定义:所需要的依赖有Servlet.class, Undertow.class, SslClientAuthMode.class
那么问题来了,SpringBoot如果这几种都有,那是怎么选择呢?从ServletWebServerFactoryAutoConfiguration配置类
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration
通过@Import就可以看出这里定义了一个顺序,依次是Tomcat->Jetty->Undertow,意思就是当环境中有Tomcat满足的依赖时就会优先使用Tomcat,依次往后推。
而一般情况下,在SpringBoot依赖中默认就已经引入tomcat的依赖,因此这里对于tomcat来说一般情况下会恒成立,那么Tomcat就会一直作为恒成立条件被SpringBoot首选为默认服务器。
二、如何配置当前web容器?
想要配置当前Web容器,可以通过yml配置让SpringBoot自动加载解析修改配置,也可以通过提供自定义的@Bean方法忽略SpringBoot自动配置采用手动配置方式。
为什么是通过@Bean提供ServletWebServerFactory和WebServerFactoryCustomizer的Bean交给Spring就可以跳过SpringBoot的自动web服务器配置呢?可从源码分析如下:
对于WebServerFactoryCustomizer在上面ServletWebServerFactoryConfiguration配置类Factory配置Tomcat,Jetty时在注解上会判断存过存在自己手动添加的ServletWebServerFactory则不再进行自动配置:
对于WebServerFactoryCustomizer则在ServletWebServerFactoryAutoConfiguration服务器自动配置类加载时,如果存在自己定义的WebServerFactoryCustomizer,那么就会触发一个WebServerFactoryCustomizerBeanPostProcessor后置处理器,在这个后置处理器中会遍历这些WebServerFactoryCustomizer并且执行内部customize方法,从而跳过自动配置,转为进行自定义配置:
三、内嵌Web服务器如何切换(从tomcat到jetty)?
上面通过源码可以知道一般情况下,Tomcat会一直作为恒成立条件被SpringBoot首选为默认服务器。
但是我们如果不想用Tomcat作为默认服务器,例如想切换为Jetty,那么我们应该怎么办呢?
我们可以把Tomcat的相关依赖在pom.xml中的spring-boot-starter-web中剔除掉,使环境不再拥有Tomcat依赖,同时加入Jetty的依赖那么就能使Jetty作为满足条件被SpringBoot选择了。
org.springframework.boot spring-boot-starter-web spring-boot-starter-tomcat org.springframework.boot org.springframework.boot spring-boot-starter-jetty
这样,SpringBoot重新启动后就会切换为Jetty服务器了。
四、Web容器怎么自动配置?
对于Web容器的自动配置,以Tomcat未来可以看上面提到的TomcatServletWebServerFactory,这是通过@Bean自动注入一个Tomcat的工厂类:
这个工厂类内部会对Tomcat进行一些初始化操作,最重要的操作在getWebServer方法内:
首先这个类是SpringBoot包提供的,用的是最底层的tomcat实例进行配置(通过new Tomcat的方式,而这个Tomcat是tomcat源码包的一个实例类 package org.apache.catalina.startup),具体的配置细节不做描述,主要对端口,协议,tomcat组件对象等进行初始化并封装:
将要发布的Web应用信息Context初始化到tomcat中:
对初始化好的tomcat进行封装并启动:
最后将这个tomcat对象封装为一个TomcatWebServer对象供SpringBoot启动时调用。
综上,web容器的自动配置,实际上是SpringBoot通过创建原生Tomcat对象,对这个对象进行端口,协议,组件等初始化,并且将Web应用信息Context对象封装到这个tomcat对象中,然后Web应用信息配置生命周期监听生效后启动tomcat,最后将这个过程
封装到一个WebServer对象中供SpringBoot启动时调用。
五、web容器启动源码解析?
SpringBoot是什么时候运行了一个web服务器呢?这个要从SpringBootApplication.run()方法进行分析。以tomcat为例按照上面提到的,这个启动过程应该会调用到TomcatServletWebServerFactory.getWebServer方法获取这么一个tomcat实例。
调用链可看下面图示:
SpringBootApplication.run():
context = createApplicationContext():创建Context环境,这个方法内会根据当前环境初始化不同的Context,如果是Web环境则会初始化出AnnotationConfigServletWebApplicationContext:
初始化AnnotationConfigServletWebApplicationContext之后,在构造函数调用这个context的refresh方法-->onRefresh方法:
调用onRefresh方法,就会调用到ServletWebServerApplicationContext的onRefresh方法,在这个方法内,就对web服务器进行了创建操作createWebServer():
在createWebServer()方法中,会判断是外置还是内置方式发布应用,分别进行不同的逻辑操作。我们这里以内置来学习:
这样,SpringBoot启动时在创建Web服务器时,就执行到了getWebServer的操作,然后再对Web服务器进行创建,初始化和启动操作。
综上:在SpringBoot的run启动时,会判断当前所处环境。
如果是Web环境则通过创建一个ServletWebServerApplicationContext,执行构造函数的refresh方法,在refresh方法内重写onRefresh方法,执行创建createWebServer()方法,这个方法会根据当前应用是内置还是外置发布方式来决定以何种方式获取web服务器。
如果是内置方式则通过TomcatServletWebServerFactory工厂类来获取一个首选的web服务器,然后进行服务器的初始化配置,应用加载生效以及服务器启动的操作。
六、SpringBoot内置服务器不使用SPI机制特别说明?
最后还有一个结论要记住:对于SpringBoot内置服务器不会通过SPI的机制(官网也有特别说明),因为SpringBoot内置服务器是SpringBoot自己帮我们创建了web服务器来发布应用,不使用SPI机制的目的就是尽可能减少内置和外置web服务器可能存在的冲突,让web应用由SpringBoot自己来管理。详细原因和原理这里不做研究。
至此,关于SpringBoot内置服务器的相关知识解析就到此了。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。