8.1.3. 嵌入式 Servlet 容器支持

对于 servlet 应用程序,Spring Boot 包括对嵌入式TomcatJettyUndertow服务器的支持。大多数开发人员使用适当的“Starter”来获取完全配置的实例。默认情况下,嵌入式服务器侦听端口 8080上的 HTTP 请求。

Servlet、过滤器和侦听器

使用嵌入式 servlet 容器时,您可以通过使用 Spring beans 或扫描 servlet 组件来注册 servlet、过滤器和 servlet 规范中的所有侦听器(例如HttpSessionListener)。

将 Servlet、过滤器和侦听器注册为 Spring Bean

任何属于Spring bean 的ServletFilter、 或 servlet*Listener实例都注册到嵌入式容器中。如果您想在配置期间引用application.properties中的值,这会特别方便。

默认情况下,如果上下文仅包含单个 Servlet,则它将映射到/. 对于多个 servlet bean,bean 名称用作路径前缀。过滤器映射到/*.

如果基于约定的映射不够灵活,您可以使用ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean类进行完全控制。

通常,让filter bean保持无序状态是安全的。如果需要特定的顺序,您应该使用@Order注释Filter或使其实现Ordered。您不能通过使用@Order 注释其 bean 方法来配置Filter 的顺序。如果您无法为Filter添加@Order或实现的类Ordered,则必须为Filter定义FilterRegistrationBean并使用 setOrder(int)方法设置注册 bean 的顺序。避免配置在 Ordered.HIGHEST_PRECEDENCE 处读取请求正文的过滤器,因为它可能会违反应用程序的字符编码配置。如果 servlet 过滤器包装请求,则应配置小于或等于 OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER 的顺序。

要查看应用程序中 每个 Filter项的顺序,请为web 日志记录组( logging.level.web=debug) 启用调试级别日志记录。已注册过滤器的详细信息(包括它们的顺序和 URL 模式)将在启动时记录下来。

注册Filter bean 时要小心,因为它们是在应用程序生命周期的早期初始化的。如果您需要注册Filter与其他 bean 交互,请考虑使用 DelegatingFilterProxyRegistrationBean来代替。

Servlet 上下文初始化

嵌入式servlet容器不直接执行jakarta.servlet.ServletContainerInitializer接口或Spring的org.springframework.web.WebApplicationInitializer接口。这是一个有意的设计决策,旨在降低设计用于在战争中运行的第三方库可能破坏 Spring Boot 应用程序的风险。

如果需要在 Spring Boot 应用程序中执行 servlet 上下文初始化,则应该注册一个实现org.springframework.boot.web.servlet.ServletContextInitializer接口的 bean。单一onStartup方法提供对 ServletContext的访问,并且如果需要,可以轻松地用作现有WebApplicationInitializer的适配器.

扫描 Servlet、过滤器和侦听器

使用嵌入式容器时,可以使用 @ServletComponentScan启用自动注册用@WebServlet@WebFilter、 和@WebListener注释的类。

@ServletComponentScan在独立容器中没有效果,由于它使用容器的内置发现机制。

ServletWebServerApplicationContext

在底层,Spring Boot 使用不同类型的ApplicationContext提供嵌入式 servlet 容器支持。ServletWebServerApplicationContext是一种特殊类型WebApplicationContext,它通过搜索单个ServletWebServerFactorybean 来引导自身。通常TomcatServletWebServerFactoryJettyServletWebServerFactory、 或UndertowServletWebServerFactory都会被自动配置。

您通常不需要了解这些实现类。大多数应用程序都是自动配置的,并且会代表您创建适当的ApplicationContextServletWebServerFactory

在嵌入式容器设置中,ServletContext被设置为服务器启动的一部分,该启动在应用程序上下文初始化期间发生。因此,ApplicationContext不能使用 ServletContext初始化中的 beans 。解决这个问题的一种方法是ApplicationContext作为 bean 的依赖项注入并ServletContext仅在需要时访问。另一种方法是在服务器启动后使用回调。这可以使用ApplicationListener监听ApplicationStartedEvent来完成,如下所示:

public class MyDemoBean implements ApplicationListener<ApplicationStartedEvent> {

    private ServletContext servletContext;

    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        this.servletContext = ((WebApplicationContext) applicationContext).getServletContext();
    }

}

自定义嵌入式 Servlet 容器

常见的 servlet 容器设置可以使用 SpringEnvironment属性进行配置。通常,您可以在您的application.propertiesapplication.yaml文件中定义属性。

常见的服务器设置包括:

  • 网络设置:传入 HTTP 请求的侦听端口 ( server.port)、要绑定的接口地址 ( server.address) 等。

  • 会话设置:会话是否持久 ( server.servlet.session.persistent)、会话超时 ( server.servlet.session.timeout)、会话数据位置 ( server.servlet.session.store-dir) 和会话 cookie 配置 ( server.servlet.session.cookie.*)。

  • 错误管理:错误页面的位置(server.error.path)等。

Spring Boot 尝试尽可能多地公开通用设置,但这并不总是可行。对于这些情况,专用命名空间提供特定于服务器的自定义(请参阅server.tomcatserver.undertow)。例如,可以使用嵌入式 servlet 容器的特定功能来配置访问日志。

请参阅ServerProperties课程以获取完整列表。

同站点 Cookie

Web 浏览器可以使用cookie属性SameSite来控制在跨站点请求中是否提交 cookie 以及如何提交。该属性与现代 Web 浏览器特别相关,现代 Web 浏览器已开始更改该属性丢失时使用的默认值。

如果您想更改会话 cookie 的SameSite属性,可以使用server.servlet.session.cookie.same-site属性。自动配置的 Tomcat、Jetty 和 Undertow 服务器支持此属性。它还用于配置基于 Spring Session servlet 的SessionRepositorybean。

例如,如果您希望会话 cookie 具有None值的SameSite属性,您可以将以下内容添加到您的application.propertiesapplication.yaml文件中:

server.servlet.session.cookie.same-site=none

如果您想更改SameSite添加到您的其他 cookie 上的属性HttpServletResponse,您可以使用CookieSameSiteSupplier. CookieSameSiteSupplier传递 a并Cookie可能返回一个SameSite值,或null

您可以使用许多便利的工厂和过滤器方法来快速匹配特定的 cookie。例如,添加以下 bean 将自动为名称与正则表达式 myapp.*匹配的所有cookie应用 LaxSameSite

@Configuration(proxyBeanMethods = false)
public class MySameSiteConfiguration {

    @Bean
    public CookieSameSiteSupplier applicationCookieSameSiteSupplier() {
        return CookieSameSiteSupplier.ofLax().whenHasNameMatching("myapp.*");
    }

}

字符编码

可以使用配置server.servlet.encoding.*属性来配置用于请求和响应处理的嵌入式 Servlet 容器的字符编码行为。

当请求的Accept-Language标头指示请求的区域设置时,servlet 容器将自动将其映射到字符集。每个容器都提供默认区域设置到字符集映射,您应该验证它们是否满足您的应用程序的需求。如果不存在,请使用server.servlet.encoding.mapping配置属性来自定义映射,如以下示例所示:

server.servlet.encoding.mapping.ko=UTF-8

在前面的示例中,ko(韩语)区域设置已映射到UTF-8. 这相当于传统war部署文件web.xml中的<locale-encoding-mapping-list>条目。

程序化定制

如果您需要以编程方式配置嵌入式 servlet 容器,您可以注册一个实现WebServerFactoryCustomizer接口的 Spring bean。 WebServerFactoryCustomizer提供对ConfigurableServletWebServerFactory 的访问,其中包括许多自定义设置器方法。以下示例显示以编程方式设置端口:

@Component
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

    @Override
    public void customize(ConfigurableServletWebServerFactory server) {
        server.setPort(9000);
    }

}

TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory是其专用变体,ConfigurableServletWebServerFactory分别为 Tomcat、Jetty 和 Undertow 提供额外的自定义 setter 方法。以下示例显示如何进行自定义TomcatServletWebServerFactory以提供对特定于 Tomcat 的配置选项的访问:

@Component
public class MyTomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory server) {
        server.addConnectorCustomizers((connector) -> connector.setAsyncTimeout(Duration.ofSeconds(20).toMillis()));
    }

}

直接自定义 ConfigurableServletWebServerFactory

对于需要从 扩展的更高级用例ServletWebServerFactory,您可以自己公开此类类型的 bean。

为许多配置选项提供了设置器。如果您需要做一些更奇特的事情,还提供了几个受保护的方法“挂钩”。详细信息请参见源代码文档。

自动配置的定制器仍然应用于您的定制工厂,因此请谨慎使用该选项。

JSP 限制

当运行使用嵌入式 servlet 容器(并打包为可执行存档)的 Spring Boot 应用程序时,JSP 支持存在一些限制。

  • 对于 Jetty 和 Tomcat,如果您使用 war 包装,它应该可以工作。可执行的 war 在使用java -jar 启动时可以工作,并且也可以部署到任何标准容器。使用可执行 jar 时不支持 JSP。

  • Undertow 不支持 JSP。

  • 创建自定义error.jsp页面不会覆盖错误处理的默认视图。 应改用自定义错误页面。

最后更新于