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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET 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?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
