> For the complete documentation index, see [llms.txt](https://doc.shiker.tech/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://doc.shiker.tech/spring-boot-can-kao-wen-dang/8.-wang-luo/8.1.-servlet-web-ying-yong-cheng-xu/8.1.3.-qian-ru-shi-servlet-rong-qi-zhi-chi.md).

# 8.1.3. 嵌入式 Servlet 容器支持

[对于 servlet 应用程序，Spring Boot 包括对嵌入式Tomcat](https://tomcat.apache.org/)、[Jetty](https://www.eclipse.org/jetty/)和[Undertow](https://github.com/undertow-io/undertow)服务器的支持。大多数开发人员使用适当的“Starter”来获取完全配置的实例。默认情况下，嵌入式服务器侦听端口 `8080`上的 HTTP 请求。

**Servlet、过滤器和侦听器**

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

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

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

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

如果基于约定的映射不够灵活，您可以使用`ServletRegistrationBean`、`FilterRegistrationBean`和`ServletListenerRegistrationBean`类进行完全控制。

通常，让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` [日志记录组](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.logging.log-groups)( `logging.level.web=debug`) 启用调试级别日志记录。已注册过滤器的详细信息（包括它们的顺序和 URL 模式）将在启动时记录下来。
>
> 注册`Filter` bean 时要小心，因为它们是在应用程序生命周期的早期初始化的。如果您需要注册`Filter`与其他 bean 交互，请考虑使用 [`DelegatingFilterProxyRegistrationBean`](https://docs.spring.io/spring-boot/docs/3.2.0/api/org/springframework/boot/web/servlet/DelegatingFilterProxyRegistrationBean.html)来代替。

**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`，它通过搜索单个`ServletWebServerFactory`bean 来引导自身。通常`TomcatServletWebServerFactory`、`JettyServletWebServerFactory`、 或`UndertowServletWebServerFactory`都会被自动配置。

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

在嵌入式容器设置中，`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 容器设置可以使用 Spring`Environment`属性进行配置。通常，您可以在您的`application.properties`或`application.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`）等。
* [SSL协议](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.webserver.configure-ssl)
* [HTTP 压缩](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.webserver.enable-response-compression)

Spring Boot 尝试尽可能多地公开通用设置，但这并不总是可行。对于这些情况，专用命名空间提供特定于服务器的自定义（请参阅`server.tomcat`和`server.undertow`）。例如，可以使用嵌入式 servlet 容器的特定功能来配置[访问日志。](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.webserver.configure-access-logs)

> 请参阅[`ServerProperties`](https://github.com/spring-projects/spring-boot/tree/v3.2.0/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java)课程以获取完整列表。

**同站点 Cookie**

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

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

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

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

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

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

```
@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);
    }
​
}
```

`TomcatServletWebServerFactory`、`JettyServletWebServerFactory`和`UndertowServletWebServerFactory`是其专用变体，`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。

为许多配置选项提供了设置器。如果您需要做一些更奇特的事情，还提供了几个受保护的方法“挂钩”。详细信息请参见[源代码文档。](https://docs.spring.io/spring-boot/docs/3.2.0/api/org/springframework/boot/web/servlet/server/ConfigurableServletWebServerFactory.html)

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

**JSP 限制**

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

* 对于 Jetty 和 Tomcat，如果您使用 war 包装，它应该可以工作。可执行的 war 在使用`java -jar` 启动时可以工作，并且也可以部署到任何标准容器。使用可执行 jar 时不支持 JSP。
* Undertow 不支持 JSP。
* 创建自定义`error.jsp`页面不会覆盖[错误处理](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#web.servlet.spring-mvc.error-handling)的默认视图。 应改用[自定义错误页面。](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#web.servlet.spring-mvc.error-handling.error-pages)
