Spring Boot「39」嵌入式容器的配置

409 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 24 天,点击查看活动详情

Spring Boot 中,默认使用嵌入式 Tomcat 作为 Web 容器。 因此,我们开发的 Servlet 应用不需要部署在外部的 Web 容器中,直接通过 java -jar 方式就能运行。 今天,我将为大家介绍一下如何配置嵌入式 Web 容器,以及如何替换默认的 Tomcat 为其他的容器,例如 Jetty。

01-Spring Boot 中配置嵌入式容器

Spring Boot 2与之前版本在接口上发生了变化,所以配置方式根据使用的 Spring Boot 版本不同而分为了两种。 第一种,Spring Boot 2之前的版本,使用 EmbeddedServletContainerCustomizer 接口:

@Component
public class CustomContainer implements EmbeddedServletContainerCustomizer {
 
    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        container.setPort(8081);
        container.setContextPath("/demo");
     }
}

第二中,Spring Boot 2以上的版本,使用 WebServerFactoryCustomizer 接口。

// 第一种
@Component
public class EmbeddedContainerCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {
    @Override
    public void customize(ConfigurableServletWebServerFactory factory) {
        factory.setPort(8888);
        factory.setContextPath("/demo");
    }
}
// 第二种
@Component
public class EmbeddedContainerCustomizer2 implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        factory.setPort(8088);
        factory.setContextPath("/demo");
    }
}

其中,TomcatServletWebServerFactory 是 ConfigurableServletWebServerFactory 接口的一个实现。

WebServerFactoryCustomizer 是 Spring Boot 定义的一个函数式接口,只有一个 customize 方法。 所有实现了该接口的类,都会被 org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor 处理。 WebServerFactoryCustomizerBeanPostProcessor 是一个 BeanPostProcessor,在 WebServerFactory 类型的 Bean 创建时会执行特殊的逻辑。

LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)  // 从 ApplicationContext 中取出所有相关的 Customizer,
        .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class) 
        .invoke((customizer) -> customizer.customize(webServerFactory));   // 执行他们的 customize 方法

02-修改默认的嵌入式容器类型

Spring Boot 默认使用嵌入式 Tomcat 作为 Web 容器。 所以,在 spring-web 中已经引用了 Tomcat 相关的依赖。

如果要替换 Web 容器类型,需要先在 spring-web 中将 Tomcat 相关的依赖移除掉。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <!--     替换底层的 servlet 容器       -->
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

然后,引入要使用的 Web 容器。例如,使用 Jetty,

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

或者使用 Undertow,

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

Spring Boot 对 Jetty 和 Undertow 的配置方式与 Tomcat 一样都做了统一的抽象,都可以使用 WebServerFactoryCustomizer 接口来配置。 只不过 Jetty 的配置类是 ConfigurableJettyWebServerFactory 或 JettyServletWebServerFactory;Undertow 的配置类是 ConfigurableUndertowWebServerFactory 或 UndertowServletWebServerFactory。 配置内容也因容器的不同,而略有不同,这里就不再继续展开介绍。

03-以程序方式启动 Tomcat

本节中,我将介绍如何通过程序的方式创建、配置并运行一个 Tomcat。

首先,添加依赖:

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-catalina</artifactId>
    <version>8.5.24</version>
</dependency>

然后,通过程序创建并启动 Tomcat:

// 创建 Tomcat 实例
Tomcat tomcat = new Tomcat();

// 设置端口号
tomcat.setPort(8081);
tomcat.setHostname("localhost");
String appBase = ".";
tomcat.getHost().setAppBase(appBase);

File docBase = new File(System.getProperty("java.io.tmpdir"));
Context context = tomcat.addContext("", docBase.getAbsolutePath());

// 设置 Servlet
Class servletClass = MyServlet.class;
Tomcat.addServlet(context, servletClass.getSimpleName(), servletClass.getName());
context.addServletMappingDecoded("/my-servlet/*", servletClass.getSimpleName());

// 启动 tomcat
tomcat.start();

我在上面的例子中向 Tomcat 中添加的 Servlet 非常简单,对所有的请求都响应一个 "Ok"。

public class MyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(HttpServletResponse.SC_OK);
        resp.getWriter().write("Ok");
        resp.getWriter().flush();
        resp.getWriter().close();
    }
}

然后,运行一下程序,访问 http://localhost:8081/my-servlet 就会得到一个 Ok 页面。

04-总结

今天,我介绍了如何配置 Spring Boot 底层适用的 Servlet 容器,然后介绍了如何替换底层的 Servlet 容器为其他类型,例如 Jetty、Undertow。 最后,我介绍了如何通过编程的方式创建并运行 Tomcat。

希望今天的内容能对你有所帮助。