pring Boot 提供了一种便捷的方式来创建独立运行的 Spring 应用程序,内嵌容器(如 Tomcat、Jetty 或 Undertow)是其核心特性之一。这使得开发者不需要将应用程序部署到外部的应用服务器上,而是通过内嵌容器直接运行应用。这种方式简化了部署流程,并且提高了开发和测试的效率。
内嵌容器的特点
独立性:应用程序包含了所有必要的依赖和服务器配置,能够独立运行,而不依赖外部的应用服务器。
简化部署:通过 Maven 或 Gradle 构建后,可以生成一个可执行的 JAR 文件,直接通过命令行运行。
一致性:开发、测试和生产环境中的运行环境一致,减少了环境差异带来的问题。
如何替换默认容器
1.pom形式
如何引入underTow作为内嵌servlet容器
在spring-boot-starter-web中排除掉tomcat的jar包
引入undertow的依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!-- 使用 Undertow 替换掉 Tomcat --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency>
观察console
2024-05-17 16:38:26.099 INFO 1108 --- [ main] io.undertow : starting server: Undertow - 2.2.16.Final 2024-05-17 16:38:26.102 INFO 1108 --- [ main] org.xnio : XNIO version 3.8.6.Final 2024-05-17 16:38:26.105 INFO 1108 --- [ main] org.xnio.nio : XNIO NIO Implementation Version 3.8.6.Final 2024-05-17 16:38:26.125 INFO 1108 --- [ main] org.jboss.threads : JBoss Threads version 3.1.0.Final 2024-05-17 16:38:26.162 INFO 1108 --- [ main] o.s.b.w.e.undertow.UndertowWebServer : Undertow started on port(s) 8080 (http) 2024-05-17 16:38:26.168 INFO 1108 --- [ main] c.a.s.SpringBootAnnotationApplication : Started SpringBootAnnotationApplication in 1.179 seconds (JVM running for 1.914)
通过日志观察,默认内嵌容器已经替换成underTow,同理jetty一样
2.主动配置
由于SpringBoot在获取servlet容器是通过ServletWebServerFactory类型来获取。ServletWebServerFactory一共有三个实现类,分别为JettyServletWebServerFactory,UndertowServletWebServerFactory,TomcatServletWebServerFactory。分别对应jetty,underTow,tomcat。
SpringBoot在AbstractApplicationContext.refresh()方法的onRefresh(),通过 ServletWebServerApplicationContext重写该方法创建webServer();
protected void onRefresh() { super.onRefresh(); try { this.createWebServer(); } catch (Throwable var2) { throw new ApplicationContextException("Unable to start web server", var2); } }
在createWebServer()中有个获取WebServerFactory方法获取对应的实现类的方法getWebServerFactory();
protected ServletWebServerFactory getWebServerFactory() { String[] beanNames = this.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class); if (beanNames.length == 0) { throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean."); } else if (beanNames.length > 1) { throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } else { return (ServletWebServerFactory)this.getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class); } }
getWebServerFactory(),通过在Spring容器中获取ServletWebServerFactory类型的bean,如果获取不到和获取多个都会抛出异常
所以我们可以通过手动配置ServletWebServerFactory对应的实现类,以此来切换不同的容器。
将tomcat,jetty,undertow的starter都引入到项目中
新建配置ServletServerConfig
@Configuration public class ServletServerConfig { @Bean public ServletWebServerFactory underTowServletWebServerFactory(){ UndertowServletWebServerFactory undertowServletWebServerFactory = new UndertowServletWebServerFactory(); undertowServletWebServerFactory.setPort(8087); return undertowServletWebServerFactory; } //@Bean public ServletWebServerFactory tomcatServletWebServerFactory(){ TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory(); tomcatServletWebServerFactory.setPort(8081); return tomcatServletWebServerFactory; } //@Bean public ServletWebServerFactory jettyServletWebServerFactory(){ JettyServletWebServerFactory jettyServletWebServerFactory = new JettyServletWebServerFactory(); jettyServletWebServerFactory.setPort(8086); return jettyServletWebServerFactory; } }
需要用哪个容器,就把其他两个容器注释掉。(不能创建多个ServletWebServerFactory,在getWebServerFactory()中会抛异常)
如何通过配置切换serlvet容器
在主动配置的基础上,可以通过对config进行改造
在application.yaml中配置servlet.server属性 ,具体1,2,3代表tomcat还是jetty由自己定义
利用SpringBoot注解@ConditionalOnProperty注解可以切换servlet容器
servlet: server: 2
@Configuration public class ServletServerConfig { @Bean @ConditionalOnProperty(name = "servlet.server",havingValue = "1") public ServletWebServerFactory underTowServletWebServerFactory(){ UndertowServletWebServerFactory undertowServletWebServerFactory = new UndertowServletWebServerFactory(); undertowServletWebServerFactory.setPort(8087); return undertowServletWebServerFactory; } @Bean @ConditionalOnProperty(name = "servlet.server",havingValue = "2") public ServletWebServerFactory tomcatServletWebServerFactory(){ TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory(); tomcatServletWebServerFactory.setPort(8081); return tomcatServletWebServerFactory; } @Bean @ConditionalOnProperty(name = "servlet.server",havingValue = "3") public ServletWebServerFactory jettyServletWebServerFactory(){ JettyServletWebServerFactory jettyServletWebServerFactory = new JettyServletWebServerFactory(); jettyServletWebServerFactory.setPort(8086); return jettyServletWebServerFactory; } }
ServletServerConfig中,1表示underTow,2表示tomdat,3,表示jetty。而application.yml中配置的2,则启动项目,观察console
2024-05-17 17:14:19.142 INFO 7500 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2024-05-17 17:14:19.148 INFO 7500 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
将配置更改为3
,再次启动,观察console
2024-05-17 17:15:01.473 INFO 5260 --- [ main] o.e.jetty.server.AbstractConnector : Started ServerConnector@13579834{HTTP/1.1, (http/1.1)}{0.0.0.0:8080} 2024-05-17 17:15:01.473 INFO 5260 --- [ main] o.s.b.web.embedded.jetty.JettyWebServer : Jetty started on port(s) 8080 (http/1.1) with context path '/'