jetty、undertow 还有 tomcat 都是 Springboot 中常见的 web 服务器。网上提到通过使用 undertow 替换 tomcat 可以一定程度上的提高性能,当然也有人做测试反驳的。这里不讨论各个 web 容器性能到底如何,那需要实践出真知。现在先尝试替换操作。

百度了一下替换相当的简单,tomcat 是从 spring-boot-starter-web 中默认引入的,所以先排除,之后再引入 undertow 即可。

Springboot:2.7.18

       <!-- springboot web 操作包,请求、路由、返回数据等 -->
        <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>

        <!-- springboot undertow 容器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
<!--            <scope>test</scope>-->
        </dependency>

一开始看到是一个 starter 的依赖,所以直接复制了 spring-boot-starter-test 改的名称,忘记去除掉 scope 标签,导致出现了下面的两种错误

一个是更新 mavan 依赖后,提示 import javax.servlet.*; 不存在。通过引入 javax.servlet-api 依赖可以暂时解决报错问题。另外还在一些博主的文章里看到引入 undertow-servlet,但打开依赖项树状图可以看到,undertow-servlet 本就是 undertow 的子依赖,但就这样在外部单独引入,javax.servlet 不存在的报错确实消失了。当时没搞懂问题所在,觉得非常的神奇。

<dependency>
    <groupId>io.undertow</groupId>
    <artifactId>undertow-servlet</artifactId>
</dependency>

另一个是在启动项目的时候,此时如果上面一个问题是通过引入 javax.servlet-api 解决的,

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>

那么就会报另一个错:

Description:

Web application could not be started as there was no org.springframework.boot.web.servlet.server.ServletWebServerFactory bean defined in the context.

Action:

Check your application's dependencies for a supported servlet web server.
Check the configured web application type.

意思就是 web 服务器没有启动成功,百度了好一会儿都没有什么解决方案。后面就发现了那个单独引入 undertow-servlet 的博文,测试如果是通过单独引入这个依赖,那么就可以正常启动,没有报错。

之后对比突然反应过来,正常引入 undertow 是不应该有 test scope 标签的,这会限制依赖项目仅仅参与测试相关的工作,包括测试代码的编译、执行。这也是为什么单独引用 undertow-servlet 就能不报错的原因。

另附使用 jetty 替换 tomcat,内容基本一致:

<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>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>