在SpringBoot中,创建一个应用后,无需添加外部服务器直接就能把项目跑在自带的内部服务器上。原因就是SpringBoot官方使用了一种非常特殊的ApplicationContext -> ServletWebServerApplication,如果当前项目是一个web项目,容器ApplicationContext就会变成Servlet web ServerApplication。它的本质也是一个Application。
原理
-
创建一个web工程,需要引入web场景就会引入tomcat场景。
-
在ServletWebServerFactoryAutoConfiguration自动配置类中
-
@Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) //在自动配置类中引入了三种ServletWebServerFactory服务器的配置类 @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration {}
-
点进这个ServletWebServerFactoryConfiguration,发现在里面写了三个WeServer工程,分别对应tomcat、Jetty、undertow三个服务器,它的自动配置是条件配置,只有当引入对应场景后配置才能生效。在对应Tomcat的类中
-
@Configuration(proxyBeanMethods = false) //ApplicationContext里面必须要有Servlet、tomcat这两个组件 @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedTomcat { @Bean //添加了一个TomcatServletWebServerFactory类型的组件。 TomcatServletWebServerFactory tomcatServletWebServerFactory( ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.getTomcatConnectorCustomizers() .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatContextCustomizers() .addAll(contextCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatProtocolHandlerCustomizers() .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList())); return factory; } }
-
可知由 ServletWebServerFactoryAutoConfiguration 引入 TomcatServletWebServerFactory,它负责根据系统中存在的场景判断生成对应的ServletWebServer【默认导入的是 spring-boot-starter-tomcat】。
-
所以说 TomcatServletWebServerFactory 最终会创建出一个TomcatWebServer【就是tomcat服务】并启动.
-
这下知道tomcat服务从哪里来之后,需要知道的就是生成一个WebServer启动服务器,去 ServleWebServerApplicationContext 里面观察,因当前是一个web应用
-
在 ServleWebServerApplicationContext 中有一个onRefresh方法,它里面只调用了创建WebServler的方法
-
@Override protected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
-
可以在createWebServer方法中打断点,启动项目观察,如何生成webServer并启动服务器的
-
在createWebServer方法中,先会创建空的webServer和空的servletContext,然后 在这一句获取webServlet
ServletWebServerFactory factory = getWebServerFactory();
,进入后会来到另一个方法中。 -
在getWebServerFactory方法中
-
//根据名字在容器中找ServletWebServerBeanFactory组件,刚好就对上了之前看过的三种不同服务器的 ServletWebServerFactory ,它又是根据容器中引入的场景进行生成对应的WebServer。这样一来就有了 TomcatServletWebServerFactory String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); //在底下的代码执行时可以看见,这个ServletWebServerFactory数量不能是0也不能大于1,就只能有一个,所以说应用引入的场景就只能有一个
-
找到对应的webServer后就会返回一个factory对象,然后给webServer赋值为tomcatServletWebServer返回的tomcatWebServer。这个webServer是一个WebServer接口,根据这个接口的实现类可以看出,Spring Boot默认支持Netty、Jetty、Tomcat、undertow【不支持jsp】这几个服务器。
-
-
this.webServer = factory.getWebServer(getSelfInitializer());
-
而在TomcatWebServer类的构造方法中,里面调用了initialize初始化方法,初始化方法中就调用了
this.tomcat.start();
启动方法,至此Tomcat就启动了 -
而在 getWebServer 方法中,启动Tomcat的方式就是以代码形式调用各种方法完成对tomcat启动的准备工作。在执行完这个方法后就把准备好的webServer服务返回,再在createWebServer方法中调用这些启动服务器的代码完成启动工作,后面再做好web资源的初始化启动。
更换Servlet容器
前面了解了Servlet容器的配置以及启动过程,这样一来就能根据SpringBoot自带的配置,通过切换服务器场景从而切换ServletWebServerFactory。
-
先排除Tomcat场景然后引入支持的那三个场景随便一个再验证是否操作成功。
-
在pom文件中
-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!-- 排除tomcat场景 --> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!-- 引入undertow场景 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>
控制台打印的信息: Undertow started on port(s) 8080 (http) 说明成功了