文章首发公众号『风象南』
使用springboot开发项目过程有可能会遇到需要更换web服务器的需求,springboot默认内置的web服务器为tomcat,现在搜到的文章都是通过排除法进行实现的,以切换为jetty为例,也就是下面这样
排除法
<dependencies>
<!--引入启动器依赖 里面就有默认的tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--排除tomcat-->
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<!--引入Jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
</dependencies>
通过排除默认的tomcat, 然后再加入新的jetty依赖,这样做虽然能实现,但是每次切换需要重新打包部署,有没有一种方式可以实现通过配置文件进行动态切换,而不用重新进行打包部署。
答案是可以的。
动态切换实现方案
经过分析源码,并验证,最终实现了动态切换效果,整体实现思路为
- 首先排除springboot默认加载web服务器的配置类 ServletWebServerFactoryAutoConfiguration
- 在配置文件自定义配置用于切换web服务器的属性,如 server.type
- 分别定义各个web服务器的自定义ServletWebServerFactory,并结合ConditionalOnProperty通过上面的server.type属性值进行动态控制该工厂类Bean的加载
- 经过以上过程,就可以实现web服务器的动态控制
Demo示例
工程结构
POM定义
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.web</groupId>
<artifactId>web-server-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.7.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<version>2.7.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
<version>2.7.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.yaml
server:
port: 9090
type: undertow # 控制切换
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
Tomcat自定义配置
package io.web.embed.tomcat;
import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
[@Configuration](https://my.oschina.net/pointdance)
@ConditionalOnProperty(name = "server.type", havingValue = "tomcat")
public class TomcatConfiguration {
@Autowired
private Environment environment;
[@Bean](https://my.oschina.net/bean)
TomcatServletWebServerFactory tomcatServletWebServerFactory(){
TomcatServletWebServerFactory webServerFactory = new TomcatServletWebServerFactory();
webServerFactory.addErrorPages(new ErrorPage("/error"));
webServerFactory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
public void customize(Connector conn) {
int port = environment.getProperty("server.port",Integer.class,8080);
conn.setPort(port);
// todo 其他自定义配置
}
});
return webServerFactory;
}
}
Jetty自定义配置
package io.web.embed.jetty;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
[@Configuration](https://my.oschina.net/pointdance)
@ConditionalOnProperty(name = "server.type", havingValue = "jetty")
public class JettyConfiguration {
@Autowired
private Environment environment;
@Bean
ServletWebServerFactory jettyServletWebServerFactory(){
JettyServletWebServerFactory jettyServletWebServerFactory = new JettyServletWebServerFactory();
jettyServletWebServerFactory.addErrorPages(new ErrorPage("/error"));
jettyServletWebServerFactory.addServerCustomizers(new JettyServerCustomizer() {
public void customize(Server server) {
ServerConnector httpConnector = new ServerConnector(server);
int port = environment.getProperty("server.port",Integer.class,8080);
httpConnector.setPort(port);
// todo 其他自定义配置
server.setConnectors(new Connector[] {httpConnector});
}
});
return jettyServletWebServerFactory;
}
}
Undertow自定义配置
package io.web.embed.undertow;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Configuration
@ConditionalOnProperty(name = "server.type", havingValue = "undertow")
public class UndertowConfiguration {
@Autowired
private Environment environment;
@Bean
UndertowServletWebServerFactory undertowServletWebServerFactory(){
int port = environment.getProperty("server.port",Integer.class,8080);
UndertowServletWebServerFactory webServerFactory = new UndertowServletWebServerFactory(port);
webServerFactory.addErrorPages(new ErrorPage("/error"));
// todo 其他自定义配置
return webServerFactory;
}
}
测试接口
package io.web.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@Autowired
private Environment environment;
@RequestMapping("/")
public String hello(){
return "hello " + environment.getProperty("server.type");
}
}
演示效果
修改配置文件 server.type 为 undertow, 启动服务器
服务已切换为 undertow
打开浏览器访问,查看切换成功。