如需获取最新稳定版本,请使用 Spring Boot 4.0.4spring-doc.cadn.net.cn

Servlet Web 应用程序

如果要构建基于 Servlet 的 Web 应用程序,您可以利用 Spring Boot 对 Spring MVC 或 Jersey 的自动配置功能。spring-doc.cadn.net.cn

“Spring Web MVC 框架”

Spring Web MVC 框架(通常简称为“Spring MVC”)是一个功能丰富的“模型-视图-控制器”(MVC)Web 框架。 Spring MVC 允许您创建特殊的 @Controller@RestController Bean 来处理传入的 HTTP 请求。 您控制器中的方法通过使用 @RequestMapping 注解映射到 HTTP 请求。spring-doc.cadn.net.cn

以下代码展示了一个典型的 @RestController,用于提供 JSON 数据:spring-doc.cadn.net.cn

import java.util.List;

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/users")
public class MyRestController {

	private final UserRepository userRepository;

	private final CustomerRepository customerRepository;

	public MyRestController(UserRepository userRepository, CustomerRepository customerRepository) {
		this.userRepository = userRepository;
		this.customerRepository = customerRepository;
	}

	@GetMapping("/{userId}")
	public User getUser(@PathVariable Long userId) {
		return this.userRepository.findById(userId).get();
	}

	@GetMapping("/{userId}/customers")
	public List<Customer> getUserCustomers(@PathVariable Long userId) {
		return this.userRepository.findById(userId).map(this.customerRepository::findByUser).get();
	}

	@DeleteMapping("/{userId}")
	public void deleteUser(@PathVariable Long userId) {
		this.userRepository.deleteById(userId);
	}

}
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController


@RestController
@RequestMapping("/users")
class MyRestController(private val userRepository: UserRepository, private val customerRepository: CustomerRepository) {

	@GetMapping("/{userId}")
	fun getUser(@PathVariable userId: Long): User {
		return userRepository.findById(userId).get()
	}

	@GetMapping("/{userId}/customers")
	fun getUserCustomers(@PathVariable userId: Long): List<Customer> {
		return userRepository.findById(userId).map(customerRepository::findByUser).get()
	}

	@DeleteMapping("/{userId}")
	fun deleteUser(@PathVariable userId: Long) {
		userRepository.deleteById(userId)
	}

}

“WebMvc.fn”(函数式变体)将路由配置与请求的实际处理分离开来,如下例所示:spring-doc.cadn.net.cn

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.function.RequestPredicate;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;

import static org.springframework.web.servlet.function.RequestPredicates.accept;
import static org.springframework.web.servlet.function.RouterFunctions.route;

@Configuration(proxyBeanMethods = false)
public class MyRoutingConfiguration {

	private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON);

	@Bean
	public RouterFunction<ServerResponse> routerFunction(MyUserHandler userHandler) {
		return route()
				.GET("/{user}", ACCEPT_JSON, userHandler::getUser)
				.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
				.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
				.build();
	}

}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.web.servlet.function.RequestPredicates.accept
import org.springframework.web.servlet.function.RouterFunction
import org.springframework.web.servlet.function.RouterFunctions
import org.springframework.web.servlet.function.ServerResponse

@Configuration(proxyBeanMethods = false)
class MyRoutingConfiguration {

	@Bean
	fun routerFunction(userHandler: MyUserHandler): RouterFunction<ServerResponse> {
		return RouterFunctions.route()
			.GET("/{user}", ACCEPT_JSON, userHandler::getUser)
			.GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
			.DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
			.build()
	}

	companion object {
		private val ACCEPT_JSON = accept(MediaType.APPLICATION_JSON)
	}

}
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;

@Component
public class MyUserHandler {

	public ServerResponse getUser(ServerRequest request) {
		...
	}

	public ServerResponse getUserCustomers(ServerRequest request) {
		...
	}

	public ServerResponse deleteUser(ServerRequest request) {
		...
	}

}
import org.springframework.stereotype.Component
import org.springframework.web.servlet.function.ServerRequest
import org.springframework.web.servlet.function.ServerResponse

@Component
class MyUserHandler {

	fun getUser(request: ServerRequest?): ServerResponse {
		...
	}

	fun getUserCustomers(request: ServerRequest?): ServerResponse {
		...
	}

	fun deleteUser(request: ServerRequest?): ServerResponse {
		...
	}

}

Spring MVC 是核心 Spring 框架的一部分,详细信息请参阅参考文档。 此外,spring.io/guides 网站上还提供了若干涵盖 Spring MVC 的指南。spring-doc.cadn.net.cn

您可以定义任意多个 RouterFunction Bean,以模块化方式定义路由器。 如果需要指定优先级顺序,可以对 Bean 进行排序。

Spring MVC 自动配置

Spring Boot 为 Spring MVC 提供了自动配置功能,该功能可与大多数应用程序良好协作。 它取代了对 @EnableWebMvc 的需求,且两者不可同时使用。 除 Spring MVC 的默认配置外,该自动配置还提供以下功能:spring-doc.cadn.net.cn

如果您希望保留这些 Spring Boot MVC 的自定义配置,并进行更多 MVC 自定义配置(例如拦截器、格式化器、视图控制器及其他功能),您可以添加一个类型为 WebMvcConfigurer 的自定义 @Configuration 类,但不能包含 @EnableWebMvcspring-doc.cadn.net.cn

如果您希望提供 RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver 的自定义实例,同时仍保留 Spring Boot MVC 的默认定制功能,则可以声明一个类型为 WebMvcRegistrations 的 Bean,并利用该 Bean 提供这些组件的自定义实例。 这些自定义实例将由 Spring MVC 进行进一步的初始化和配置。 如需参与该后续处理过程,或按需覆盖该处理逻辑,则应使用 WebMvcConfigurerspring-doc.cadn.net.cn

如果您不想使用自动配置,而希望完全掌控 Spring MVC,请添加您自己的、使用 @Configuration 注解的类。 或者,按照 @EnableWebMvc API 文档中的说明,添加您自己的、使用 @Configuration 注解的 DelegatingWebMvcConfiguration 类。spring-doc.cadn.net.cn

Spring MVC 转换服务

Spring MVC 使用的 ConversionService 与用于从您的 application.propertiesapplication.yaml 文件中转换值的 ConversionService 不同。 这意味着 PeriodDurationDataSize 转换器不可用,且 @DurationUnit@DataSizeUnit 注解将被忽略。spring-doc.cadn.net.cn

如果您希望自定义 Spring MVC 所使用的 ConversionService,可以提供一个带有 addFormatters 方法的 WebMvcConfigurer Bean。 通过该方法,您可以注册任意所需的转换器,或者委托给 ApplicationConversionService 上提供的静态方法。spring-doc.cadn.net.cn

还可以通过 spring.mvc.format.* 配置属性来自定义转换。 若未进行配置,则使用以下默认值:spring-doc.cadn.net.cn

属性 DateTimeFormatter 格式

spring.mvc.format.datespring-doc.cadn.net.cn

ofLocalizedDate(FormatStyle.SHORT)spring-doc.cadn.net.cn

java.util.DateLocalDatespring-doc.cadn.net.cn

spring.mvc.format.timespring-doc.cadn.net.cn

ofLocalizedTime(FormatStyle.SHORT)spring-doc.cadn.net.cn

java.time 的 LocalTimeOffsetTimespring-doc.cadn.net.cn

spring.mvc.format.date-timespring-doc.cadn.net.cn

ofLocalizedDateTime(FormatStyle.SHORT)spring-doc.cadn.net.cn

java.time 的 LocalDateTimeOffsetDateTimeZonedDateTimespring-doc.cadn.net.cn

HTTP消息转换器

Spring MVC 使用 HttpMessageConverter 接口来转换 HTTP 请求和响应。 开箱即用,已包含合理的默认配置。 例如,对象可自动转换为 JSON(通过使用 Jackson 库)或 XML(若 Jackson XML 扩展可用,则使用该扩展;否则使用 JAXB)。 默认情况下,字符串采用 UTF-8 编码。spring-doc.cadn.net.cn

上下文中存在的任意 HttpMessageConverter Bean 都会被添加到转换器列表中。 您也可以以相同方式覆盖默认转换器。spring-doc.cadn.net.cn

如果需要添加或自定义转换器,可以使用 Spring Boot 的 HttpMessageConverters 类,如下例所示:spring-doc.cadn.net.cn

import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;

@Configuration(proxyBeanMethods = false)
public class MyHttpMessageConvertersConfiguration {

	@Bean
	public HttpMessageConverters customConverters() {
		HttpMessageConverter<?> additional = new AdditionalHttpMessageConverter();
		HttpMessageConverter<?> another = new AnotherHttpMessageConverter();
		return new HttpMessageConverters(additional, another);
	}

}
import org.springframework.boot.autoconfigure.http.HttpMessageConverters
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.HttpMessageConverter

@Configuration(proxyBeanMethods = false)
class MyHttpMessageConvertersConfiguration {

	@Bean
	fun customConverters(): HttpMessageConverters {
		val additional: HttpMessageConverter<*> = AdditionalHttpMessageConverter()
		val another: HttpMessageConverter<*> = AnotherHttpMessageConverter()
		return HttpMessageConverters(additional, another)
	}

}

如需进一步控制,您还可以对 HttpMessageConverters 进行子类化,并重写其 postProcessConverters 和/或 postProcessPartConverters 方法。 当您希望重新排序或移除 Spring MVC 默认配置的某些转换器时,此方法非常有用。spring-doc.cadn.net.cn

消息代码解析器

Spring MVC 提供了一种策略,用于根据绑定错误生成渲染错误消息所需的错误代码:MessageCodesResolver。 如果您设置了 spring.mvc.message-codes-resolver-format 属性 PREFIX_ERROR_CODEPOSTFIX_ERROR_CODE,Spring Boot 会为您自动创建一个(参见 DefaultMessageCodesResolver.Format 中的枚举)。spring-doc.cadn.net.cn

静态内容

默认情况下,Spring Boot 从类路径中名为 /static(或 /public/resources/META-INF/resources)的目录,或从 ServletContext 的根目录提供静态内容。 它使用 Spring MVC 中的 ResourceHttpRequestHandler,因此您可以通过添加自己的 WebMvcConfigurer 并重写 addResourceHandlers 方法来自定义该行为。spring-doc.cadn.net.cn

在独立的 Web 应用程序中,容器的默认 Servlet 未启用。 可通过 server.servlet.register-default-servlet 属性启用该默认 Servlet。spring-doc.cadn.net.cn

默认 Servlet 充当后备机制,当 Spring 决定不处理某个请求时,它将从 ServletContext 的根目录提供内容。 大多数情况下,这种情况不会发生(除非您修改了默认的 MVC 配置),因为 Spring 始终可以通过 DispatcherServlet 处理请求。spring-doc.cadn.net.cn

默认情况下,资源映射到 /**,但您可以通过 spring.mvc.static-path-pattern 属性来调整该设置。 例如,将所有资源重新定位到 /resources/** 可按如下方式实现:spring-doc.cadn.net.cn

spring.mvc.static-path-pattern=/resources/**
spring:
  mvc:
    static-path-pattern: "/resources/**"

您还可以通过使用 spring.web.resources.static-locations 属性来自定义静态资源位置(以目录路径列表替换默认值)。 根 Servlet 上下文路径 "/" 也会自动被添加为一个位置。spring-doc.cadn.net.cn

除了前述“标准”静态资源位置外,Webjars 内容属于一种特殊情况。 默认情况下,任何路径为 /webjars/** 的资源,若以 Webjars 格式打包在 JAR 文件中,则将直接从该 JAR 文件中提供服务。 该路径可通过 spring.mvc.webjars-path-pattern 属性进行自定义。spring-doc.cadn.net.cn

如果您的应用程序被打包为 JAR 文件,请勿使用 src/main/webapp 目录。 尽管该目录是一种常见标准,但它**仅**适用于 WAR 打包方式;当您生成 JAR 文件时,大多数构建工具会静默忽略该目录。

Spring Boot 还支持 Spring MVC 提供的高级资源处理功能,可用于实现静态资源的缓存失效(cache-busting)或为 Webjars 使用与版本无关的 URL 等场景。spring-doc.cadn.net.cn

若要为 Webjars 使用与版本无关的 URL,请添加 org.webjars:webjars-locator-lite 依赖项。 然后声明您的 Webjar。 以 jQuery 为例,添加 "/webjars/jquery/jquery.min.js" 后将生成 "/webjars/jquery/x.y.z/jquery.min.js",其中 x.y.z 为 Webjar 的版本号。spring-doc.cadn.net.cn

要使用缓存清除(cache busting),以下配置为所有静态资源设置了一个缓存清除解决方案,从而在 URL 中有效添加内容哈希(例如 <link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>):spring-doc.cadn.net.cn

spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**
spring:
  web:
    resources:
      chain:
        strategy:
          content:
            enabled: true
            paths: "/**"
借助为 Thymeleaf 和 FreeMarker 自动配置的 ResourceUrlEncodingFilter,模板中的资源链接会在运行时被重写。 若使用 JSP,则需手动声明此过滤器。 其他模板引擎目前未提供自动支持,但可通过自定义模板宏/辅助方法并结合使用 ResourceUrlProvider 来实现支持。

当使用 JavaScript 模块加载器等机制动态加载资源时,重命名文件并不可行。 因此,框架还支持其他策略,并且这些策略可以组合使用。 “固定”策略("fixed" strategy)会在 URL 中添加一个静态版本字符串,而不会更改文件名,如下例所示:spring-doc.cadn.net.cn

spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**
spring.web.resources.chain.strategy.fixed.enabled=true
spring.web.resources.chain.strategy.fixed.paths=/js/lib/
spring.web.resources.chain.strategy.fixed.version=v12
spring:
  web:
    resources:
      chain:
        strategy:
          content:
            enabled: true
            paths: "/**"
          fixed:
            enabled: true
            paths: "/js/lib/"
            version: "v12"

通过此配置,位于 "/js/lib/" 下的 JavaScript 模块采用固定版本控制策略("/v12/js/lib/mymodule.js"),而其他资源仍采用基于内容的版本控制策略(<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>)。spring-doc.cadn.net.cn

有关更多支持的选项,请参见 WebProperties.Resourcesspring-doc.cadn.net.cn

该功能已在一篇专门的博客文章以及Spring框架的参考文档中进行了详尽阐述。spring-doc.cadn.net.cn

欢迎页面

Spring Boot 支持静态欢迎页和模板化欢迎页。 它首先在已配置的静态内容位置中查找 index.html 文件。 如果未找到该文件,则接着查找 index 模板。 只要找到其中任意一个,便会自动将其用作应用程序的欢迎页。spring-doc.cadn.net.cn

此设置仅作为应用程序所定义的实际索引路由的备用方案。 排序顺序由 `HandlerMapping` 类型 Bean 的声明顺序决定,默认顺序如下:spring-doc.cadn.net.cn

RouterFunctionMappingspring-doc.cadn.net.cn

使用 RouterFunction Bean 声明的端点spring-doc.cadn.net.cn

RequestMappingHandlerMappingspring-doc.cadn.net.cn

@Controller 个 Bean 中声明的端点spring-doc.cadn.net.cn

WelcomePageHandlerMappingspring-doc.cadn.net.cn

欢迎页支持spring-doc.cadn.net.cn

自定义网站图标

与其他静态资源一样,Spring Boot 会在已配置的静态内容位置中查找 favicon.ico 文件。 如果存在该文件,则会自动将其用作应用程序的网站图标(favicon)。spring-doc.cadn.net.cn

路径匹配与内容协商

Spring MVC 可通过检查请求路径,并将其与应用程序中定义的映射(例如,控制器方法上的 @RequestMapping 注解)进行匹配,从而将传入的 HTTP 请求映射到相应的处理器。spring-doc.cadn.net.cn

Spring Boot 默认禁用后缀模式匹配,这意味着类似 "GET /projects/spring-boot.json" 的请求将不会匹配到 @GetMapping("/projects/spring-boot") 的映射。 这被视为 Spring MVC 应用程序的最佳实践。 该功能在过去主要用于那些未发送正确“Accept”请求头的 HTTP 客户端;我们需要确保向客户端发送正确的内容类型(Content Type)。 如今,内容协商(Content Negotiation)已变得更加可靠。spring-doc.cadn.net.cn

还有其他方法可处理那些未能始终发送正确“Accept”请求头的HTTP客户端。 除了使用后缀匹配外,我们还可以使用查询参数,以确保类似 "GET /projects/spring-boot?format=json" 的请求被映射到 @GetMapping("/projects/spring-boot")spring-doc.cadn.net.cn

spring.mvc.contentnegotiation.favor-parameter=true
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true

或者,如果您希望使用其他参数名称:spring-doc.cadn.net.cn

spring.mvc.contentnegotiation.favor-parameter=true
spring.mvc.contentnegotiation.parameter-name=myparam
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true
      parameter-name: "myparam"

大多数标准媒体类型都开箱即用,但您也可以定义新的媒体类型:spring-doc.cadn.net.cn

spring.mvc.contentnegotiation.media-types.markdown=text/markdown
spring:
  mvc:
    contentnegotiation:
      media-types:
        markdown: "text/markdown"

自 Spring Framework 5.3 起,Spring MVC 支持两种将请求路径匹配到控制器的策略。 默认情况下,Spring Boot 使用 PathPatternParser 策略。 PathPatternParser 是一种 优化实现,但与 AntPathMatcher 策略相比存在一些限制。 PathPatternParser 限制了部分路径模式变体的使用。 此外,它还无法与为 DispatcherServlet 配置路径前缀(spring.mvc.servlet.path)的方式兼容。spring-doc.cadn.net.cn

该策略可通过 spring.mvc.pathmatch.matching-strategy 配置属性进行配置,如下例所示:spring-doc.cadn.net.cn

spring.mvc.pathmatch.matching-strategy=ant-path-matcher
spring:
  mvc:
    pathmatch:
      matching-strategy: "ant-path-matcher"

如果找不到请求的处理器,Spring MVC 将抛出一个 NoHandlerFoundException。 请注意,默认情况下,静态资源服务 映射到 /**,因此将为所有请求提供一个处理器。 如果没有可用的静态资源,ResourceHttpRequestHandler 将抛出一个 NoResourceFoundException。 若要抛出 NoHandlerFoundException,请将 spring.mvc.static-path-pattern 设置为更具体的值(例如 /resources/**),或设置 spring.web.resources.add-mappingsfalse 以完全禁用静态资源服务。spring-doc.cadn.net.cn

可配置的Web绑定初始化器

Spring MVC 使用一个 WebBindingInitializer 为特定请求初始化一个 WebDataBinder。 如果您创建了自己的 ConfigurableWebBindingInitializer @Bean,Spring Boot 将自动配置 Spring MVC 以使用它。spring-doc.cadn.net.cn

模板引擎

除了 REST Web 服务外,您还可以使用 Spring MVC 来提供动态 HTML 内容。 Spring MVC 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 JSP。 此外,许多其他模板引擎也提供了各自与 Spring MVC 的集成支持。spring-doc.cadn.net.cn

Spring Boot 包含对以下模板引擎的自动配置支持:spring-doc.cadn.net.cn

如果可能,应避免使用 JSP。 在嵌入式 Servlet 容器中使用 JSP 时存在若干已知限制

当您使用这些模板引擎之一并采用默认配置时,模板会自动从 src/main/resources/templates 中加载。spring-doc.cadn.net.cn

具体取决于您运行应用程序的方式,您的集成开发环境(IDE)可能会以不同的顺序排列类路径(classpath)。 在 IDE 中通过主方法(main method)运行应用程序时,其类路径的顺序与使用 Maven 或 Gradle 运行应用程序、或从打包后的 JAR 文件运行应用程序时的顺序不同。 这可能导致 Spring Boot 无法找到预期的模板。 如果您遇到此问题,可以在 IDE 中重新调整类路径顺序,将模块的类文件和资源置于最前面。

错误处理

默认情况下,Spring Boot 提供了一个 /error 映射,以合理的方式处理所有错误,并将其注册为 Servlet 容器中的“全局”错误页面。 对于机器客户端,它会生成一个包含错误详情、HTTP 状态码及异常消息的 JSON 响应。 对于浏览器客户端,则提供一个“白标”(whitelabel)错误视图,以 HTML 格式渲染相同的数据(如需自定义该视图,请添加一个 View,使其解析为 error)。spring-doc.cadn.net.cn

如果要自定义默认的错误处理行为,可以设置若干个 server.error 属性。 请参阅附录中的 服务器属性 章节。spring-doc.cadn.net.cn

若要完全替换默认行为,您可以实现 ErrorController,并注册该类型的 Bean 定义;或者添加一个类型为 ErrorAttributes 的 Bean,以在沿用现有机制的同时替换其内容。spring-doc.cadn.net.cn

BasicErrorController 可用作自定义 ErrorController 的基类。 当您希望为一种新内容类型添加处理器时(默认仅专门处理 text/html,并对其他所有类型提供回退机制),此方式尤为有用。 为此,请继承 BasicErrorController,添加一个带有 @RequestMapping 的公共方法,且该注解需具有 produces 属性,然后为您新建的类型创建一个 Bean。

自 Spring Framework 6.0 起,已支持 RFC 9457 问题详情(Problem Details)。 Spring MVC 可以生成采用 application/problem+json 媒体类型的自定义错误消息,例如:spring-doc.cadn.net.cn

{
	"type": "https://example.org/problems/unknown-project",
	"title": "Unknown project",
	"status": 404,
	"detail": "No project found for id 'spring-unknown'",
	"instance": "/projects/spring-unknown"
}

可通过将 spring.mvc.problemdetails.enabled 设置为 true 来启用此支持。spring-doc.cadn.net.cn

您还可以定义一个使用 @ControllerAdvice 注解的类,以自定义特定控制器和/或异常类型的 JSON 响应文档,如下例所示:spring-doc.cadn.net.cn

import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@ControllerAdvice(basePackageClasses = SomeController.class)
public class MyControllerAdvice extends ResponseEntityExceptionHandler {

	@ResponseBody
	@ExceptionHandler(MyException.class)
	public ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
		HttpStatus status = getStatus(request);
		return new ResponseEntity<>(new MyErrorBody(status.value(), ex.getMessage()), status);
	}

	private HttpStatus getStatus(HttpServletRequest request) {
		Integer code = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
		HttpStatus status = HttpStatus.resolve(code);
		return (status != null) ? status : HttpStatus.INTERNAL_SERVER_ERROR;
	}

}
import jakarta.servlet.RequestDispatcher
import jakarta.servlet.http.HttpServletRequest
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.ResponseBody
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler

@ControllerAdvice(basePackageClasses = [SomeController::class])
class MyControllerAdvice : ResponseEntityExceptionHandler() {

	@ResponseBody
	@ExceptionHandler(MyException::class)
	fun handleControllerException(request: HttpServletRequest, ex: Throwable): ResponseEntity<*> {
		val status = getStatus(request)
		return ResponseEntity(MyErrorBody(status.value(), ex.message), status)
	}

	private fun getStatus(request: HttpServletRequest): HttpStatus {
		val code = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE) as Int
		val status = HttpStatus.resolve(code)
		return status ?: HttpStatus.INTERNAL_SERVER_ERROR
	}

}

在前述示例中,如果与 SomeController 位于同一包中的控制器抛出 MyException,则将使用 MyErrorBody POJO 的 JSON 表示形式,而非 ErrorAttributes 的表示形式。spring-doc.cadn.net.cn

在某些情况下,控制器级别处理的错误不会被 Web 观测功能或指标基础设施记录。 应用程序可通过在观测上下文中设置已处理的异常,确保此类异常被观测功能记录。spring-doc.cadn.net.cn

自定义错误页面

如果希望为指定的状态码显示自定义 HTML 错误页面,可以将文件添加到 /error 目录中。 错误页面既可以是静态 HTML(即,置于任意静态资源目录下),也可以使用模板生成。 文件名应为精确的状态码,或采用状态码通配符格式。spring-doc.cadn.net.cn

例如,要将 404 映射到一个静态 HTML 文件,您的目录结构应如下所示:spring-doc.cadn.net.cn

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

要使用 FreeMarker 模板映射所有 5xx 错误,您的目录结构如下所示:spring-doc.cadn.net.cn

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.ftlh
             +- <other templates>

对于更复杂的映射,您还可以添加实现 ErrorViewResolver 接口的 Bean,如下例所示:spring-doc.cadn.net.cn

import java.util.Map;

import jakarta.servlet.http.HttpServletRequest;

import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.ModelAndView;

public class MyErrorViewResolver implements ErrorViewResolver {

	@Override
	public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
		// Use the request or status to optionally return a ModelAndView
		if (status == HttpStatus.INSUFFICIENT_STORAGE) {
			// We could add custom model values here
			new ModelAndView("myview");
		}
		return null;
	}

}
import jakarta.servlet.http.HttpServletRequest
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver
import org.springframework.http.HttpStatus
import org.springframework.web.servlet.ModelAndView

class MyErrorViewResolver : ErrorViewResolver {

	override fun resolveErrorView(request: HttpServletRequest, status: HttpStatus,
			model: Map<String, Any>): ModelAndView? {
		// Use the request or status to optionally return a ModelAndView
		if (status == HttpStatus.INSUFFICIENT_STORAGE) {
			// We could add custom model values here
			return ModelAndView("myview")
		}
		return null
	}

}

您还可以使用常规的 Spring MVC 功能,例如 @ExceptionHandler 方法@ControllerAdviceErrorController 随后会捕获任何未处理的异常。spring-doc.cadn.net.cn

在 Spring MVC 之外映射错误页面

对于不使用 Spring MVC 的应用程序,您可以使用 ErrorPageRegistrar 接口直接注册 ErrorPage 实例。 该抽象层直接与底层嵌入式 Servlet 容器交互,即使您没有 Spring MVC 的 DispatcherServlet,它也能正常工作。spring-doc.cadn.net.cn

import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

@Configuration(proxyBeanMethods = false)
public class MyErrorPagesConfiguration {

	@Bean
	public ErrorPageRegistrar errorPageRegistrar() {
		return this::registerErrorPages;
	}

	private void registerErrorPages(ErrorPageRegistry registry) {
		registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
	}

}
import org.springframework.boot.web.server.ErrorPage
import org.springframework.boot.web.server.ErrorPageRegistrar
import org.springframework.boot.web.server.ErrorPageRegistry
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpStatus

@Configuration(proxyBeanMethods = false)
class MyErrorPagesConfiguration {

	@Bean
	fun errorPageRegistrar(): ErrorPageRegistrar {
		return ErrorPageRegistrar { registry: ErrorPageRegistry -> registerErrorPages(registry) }
	}

	private fun registerErrorPages(registry: ErrorPageRegistry) {
		registry.addErrorPages(ErrorPage(HttpStatus.BAD_REQUEST, "/400"))
	}

}
如果注册了一个 ErrorPage,其路径最终由某个 Filter 处理(这在某些非 Spring 的 Web 框架(例如 Jersey 和 Wicket)中较为常见),则该 Filter 必须显式注册为一个 ERROR 分发器,如下例所示:
import java.util.EnumSet;

import jakarta.servlet.DispatcherType;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MyFilterConfiguration {

	@Bean
	public FilterRegistrationBean<MyFilter> myFilter() {
		FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(new MyFilter());
		// ...
		registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
		return registration;
	}

}
import jakarta.servlet.DispatcherType
import org.springframework.boot.web.servlet.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.util.EnumSet

@Configuration(proxyBeanMethods = false)
class MyFilterConfiguration {

	@Bean
	fun myFilter(): FilterRegistrationBean<MyFilter> {
		val registration = FilterRegistrationBean(MyFilter())
		// ...
		registration.setDispatcherTypes(EnumSet.allOf(DispatcherType::class.java))
		return registration
	}

}

请注意,默认的 FilterRegistrationBean 不包含 ERROR 分发器类型。spring-doc.cadn.net.cn

WAR 部署中的错误处理

当部署到 Servlet 容器时,Spring Boot 使用其错误页面过滤器(error page filter),将带有错误状态的请求转发至相应的错误页面。 这是因为 Servlet 规范并未提供用于注册错误页面的 API。 根据您所部署 WAR 文件的目标容器以及应用程序所使用的技术,可能需要进行一些额外的配置。spring-doc.cadn.net.cn

错误页面过滤器仅在响应尚未提交时,才能将请求转发到正确的错误页面。 默认情况下,WebSphere Application Server 8.0 及更高版本会在 Servlet 的 service 方法成功执行完毕后提交响应。 您应通过将 com.ibm.ws.webcontainer.invokeFlushAfterService 设置为 false 来禁用此行为。spring-doc.cadn.net.cn

CORS 支持

跨域资源共享(CORS)是一项由W3C 制定的规范大多数浏览器均已实现该规范。它允许您以灵活的方式指定哪些类型的跨域请求被授权,从而替代了 IFRAME 或 JSONP 等安全性较低、功能较弱的方法。spring-doc.cadn.net.cn

自 4.2 版本起,Spring MVC 支持 CORS。 在 Spring Boot 应用程序中,使用 控制器方法级 CORS 配置 并配合 @CrossOrigin 注解,无需任何特定配置。 可通过注册一个自定义的 WebMvcConfigurer Bean,并实现自定义的 addCorsMappings(CorsRegistry) 方法,来定义 全局 CORS 配置,如下例所示:spring-doc.cadn.net.cn

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration(proxyBeanMethods = false)
public class MyCorsConfiguration {

	@Bean
	public WebMvcConfigurer corsConfigurer() {
		return new WebMvcConfigurer() {

			@Override
			public void addCorsMappings(CorsRegistry registry) {
				registry.addMapping("/api/**");
			}

		};
	}

}
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.CorsRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration(proxyBeanMethods = false)
class MyCorsConfiguration {

	@Bean
	fun corsConfigurer(): WebMvcConfigurer {
		return object : WebMvcConfigurer {
			override fun addCorsMappings(registry: CorsRegistry) {
				registry.addMapping("/api/**")
			}
		}
	}

}

JAX-RS 和 Jersey

如果您更倾向于使用 JAX-RS 编程模型来构建 REST 端点,可以选用现有的某个实现,而不必使用 Spring MVC。 JerseyApache CXF 均可开箱即用,效果良好。 CXF 要求您在应用上下文中将它的 ServletFilter 注册为一个 @Bean。 Jersey 提供了一些原生的 Spring 支持,因此我们也在 Spring Boot 中为其提供了自动配置支持,并配套提供了Starters(starter)。spring-doc.cadn.net.cn

要开始使用 Jersey,请将 spring-boot-starter-jersey 作为依赖项引入,然后您需要一个类型为 ResourceConfig@Bean 实例,并在其中注册所有端点,如下例所示:spring-doc.cadn.net.cn

import org.glassfish.jersey.server.ResourceConfig;

import org.springframework.stereotype.Component;

@Component
public class MyJerseyConfig extends ResourceConfig {

	public MyJerseyConfig() {
		register(MyEndpoint.class);
	}

}
Jersey 对可执行归档文件的扫描支持相当有限。 例如,在运行可执行的 WAR 文件时,它无法扫描位于 完全可执行的 JAR 文件 中或位于 WEB-INF/classes 中的包内的端点。 为避免此限制,不应使用 packages 方法,而应如前述示例所示,通过 register 方法单独注册各个端点。

如需进行更高级的自定义,您还可以注册任意数量的、实现 ResourceConfigCustomizer 接口的 Bean。spring-doc.cadn.net.cn

所有已注册的端点都应为带有 HTTP 资源注解(如 @GET 及其他)的 @Component,如下例所示:spring-doc.cadn.net.cn

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

import org.springframework.stereotype.Component;

@Component
@Path("/hello")
public class MyEndpoint {

	@GET
	public String message() {
		return "Hello";
	}

}

由于 @Endpoint 是一个 Spring @Component,其生命周期由 Spring 管理,因此您可以使用 @Autowired 注解来注入依赖项,并使用 @Value 注解来注入外部配置。 默认情况下,Jersey Servlet 已注册并映射到 /*。 您可以通过在 ResourceConfig 中添加 @ApplicationPath 来更改该映射。spring-doc.cadn.net.cn

默认情况下,Jersey 会配置为一个类型为 @BeanServletRegistrationBean 中的 Servlet,其名称为 jerseyServletRegistration。 默认情况下,该 Servlet 采用延迟初始化方式,但您可以通过设置 spring.jersey.servlet.load-on-startup 来自定义此行为。 您可以通过创建一个同名的自定义 Bean 来禁用或覆盖该 Bean。 您也可以通过设置 spring.jersey.type=filter 来使用 Filter 替代 Servlet(此时,用于替换或覆盖的 @BeanjerseyFilterRegistration)。 该 Filter 具有一个 @Order,您可通过 spring.jersey.filter.order 进行设置。 当以 Filter 方式使用 Jersey 时,必须存在一个 Servlet 来处理所有未被 Jersey 拦截的请求。 如果您的应用程序中不包含此类 Servlet,则可通过将 server.servlet.register-default-servlet 设置为 true 来启用默认 Servlet。 Servlet 和 Filter 的注册均可通过使用 spring.jersey.init.* 指定属性映射来提供初始化参数。spring-doc.cadn.net.cn

嵌入式Servlet容器支持

对于 Servlet 应用程序,Spring Boot 提供了对嵌入式 TomcatJettyUndertow 服务器的支持。 大多数开发者会使用相应的 Starter 来获取已完全配置的实例。 默认情况下,嵌入式服务器在端口 8080 上监听 HTTP 请求。spring-doc.cadn.net.cn

Servlet、过滤器和监听器

使用嵌入式 Servlet 容器时,您可以通过 Spring Bean 或扫描 Servlet 组件的方式,注册 Servlet、过滤器以及 Servlet 规范中的所有监听器(例如 HttpSessionListener)。spring-doc.cadn.net.cn

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

任何实现了 ServletFilter 接口或继承自 servlet *Listener 的 Spring Bean 实例,都将自动注册到嵌入式容器中。 如果希望在配置过程中引用 application.properties 中的某个值,这种方式将尤为便捷。spring-doc.cadn.net.cn

默认情况下,如果上下文中仅包含一个 Servlet,则它将映射到 /。 当存在多个 Servlet Bean 时,Bean 名称将用作路径前缀。 过滤器(Filter)映射到 /*spring-doc.cadn.net.cn

如果基于约定的映射方式灵活性不足,您可以使用 ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean 类来实现完全控制。 如果您更倾向于使用注解而非 ServletRegistrationBeanFilterRegistrationBean,您也可以选择使用 @ServletRegistration@FilterRegistration 作为替代方案。spring-doc.cadn.net.cn

通常可以安全地让过滤器 Bean 保持无序状态。 如果需要指定特定顺序,则应使用 Filter 注解 @Order,或使其实现 Ordered。 您无法通过在 Filter 的 Bean 方法上添加 @Order 注解来配置其顺序。 如果您无法修改 Filter 类以添加 @Order 或实现 Ordered,则必须为 FilterRegistrationBean 定义一个 Filter,并使用 setOrder(int) 方法设置注册 Bean 的顺序。 或者,如果您更倾向于使用注解,也可以使用 @FilterRegistration 并设置 order 属性。 请避免在 Ordered.HIGHEST_PRECEDENCE 处配置读取请求体的过滤器,因为这可能与应用程序的字符编码配置相冲突。 如果 Servlet 过滤器包装了请求,则其配置顺序应小于或等于 OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDERspring-doc.cadn.net.cn

要查看应用程序中每个 Filter 的执行顺序,请为 web 日志组logging.level.web=debug)启用调试级别日志记录。 注册的过滤器详细信息(包括其执行顺序和 URL 模式)将在应用启动时记录到日志中。
注册 Filter 类型的 Bean 时需谨慎,因为它们会在应用程序生命周期的非常早期即被初始化。 如果您需要注册一个需与其他 Bean 进行交互的 Filter,请考虑改用 DelegatingFilterProxyRegistrationBean

Servlet 上下文初始化

嵌入式 Servlet 容器不会直接执行 ServletContainerInitializer 接口或 Spring 的 WebApplicationInitializer 接口。 这是有意为之的设计决策,旨在降低专为 WAR 环境设计的第三方库破坏 Spring Boot 应用程序的风险。spring-doc.cadn.net.cn

如果需要在 Spring Boot 应用程序中执行 Servlet 上下文初始化,应注册一个实现 ServletContextInitializer 接口的 Bean。 该接口唯一的 onStartup 方法可访问 ServletContext,并且在必要时可轻松用作现有 ServletContainerInitializer 的适配器。spring-doc.cadn.net.cn

初始化参数

初始化参数可在 ServletContext 上通过 server.servlet.context-parameters.* 属性进行配置。 例如,属性 server.servlet.context-parameters.com.example.parameter=example 将配置一个名为 com.example.parameter、值为 exampleServletContext 初始化参数。spring-doc.cadn.net.cn

扫描 Servlet、过滤器和监听器

使用嵌入式容器时,可通过使用 @ServletComponentScan 启用对标注了 @WebServlet@WebFilter@WebListener 的类的自动注册。spring-doc.cadn.net.cn

@ServletComponentScan 在独立容器中无效,此时将使用容器内置的发现机制。

ServletWebServerApplicationContext

在底层,Spring Boot 为嵌入式 Servlet 容器支持使用了一种不同类型的 ApplicationContextServletWebServerApplicationContext 是一种特殊的 WebApplicationContext,它通过查找单个 ServletWebServerFactory Bean 来实现自我引导。 通常情况下,TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory 已被自动配置。spring-doc.cadn.net.cn

您通常无需关注这些实现类。 大多数应用程序会自动配置,相应的 ApplicationContextServletWebServerFactory 将由框架为您创建。

在嵌入式容器配置中,ServletContext 会在应用上下文初始化期间的服务器启动过程中被设置。 正因如此,ApplicationContext 中的 Bean 无法可靠地通过 ServletContext 进行初始化。 一种绕过该问题的方法是:将 ApplicationContext 作为 Bean 的依赖项注入,并仅在实际需要时访问 ServletContext。 另一种方法是在服务器启动完成后使用回调机制。 这可以通过一个 ApplicationListener 实现,该组件监听 ApplicationStartedEvent,具体方式如下:spring-doc.cadn.net.cn

import jakarta.servlet.ServletContext;

import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.web.context.WebApplicationContext;

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.propertiesapplication.yaml 文件中定义这些属性。spring-doc.cadn.net.cn

常见的服务器设置包括:spring-doc.cadn.net.cn

Spring Boot 尽可能地暴露常用配置,但并非所有情况下都能做到这一点。 对于这些情况,专用的命名空间提供了针对特定服务器的自定义功能(参见 server.tomcatserver.undertow)。 例如,访问日志 可通过嵌入式 Servlet 容器的特定功能进行配置。spring-doc.cadn.net.cn

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

SameSite Cookie

SameSite Cookie 属性可用于 Web 浏览器,以控制 Cookie 在跨站请求中是否提交以及如何提交。 该属性对现代 Web 浏览器尤为重要,因为这些浏览器已开始更改在缺失该属性时所采用的默认值。spring-doc.cadn.net.cn

如果您希望修改会话 Cookie 的 SameSite 属性,可以使用 server.servlet.session.cookie.same-site 属性。 该属性受自动配置的 Tomcat、Jetty 和 Undertow 服务器支持。 它还用于配置基于 Spring Session Servlet 的 SessionRepository Bean。spring-doc.cadn.net.cn

例如,如果您希望会话 Cookie 的 SameSite 属性值为 None,则可以在您的 application.propertiesapplication.yaml 文件中添加以下配置:spring-doc.cadn.net.cn

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

如果您希望修改添加到您的 HttpServletResponse 中的其他 Cookie 的 SameSite 属性,可以使用 CookieSameSiteSupplierCookieSameSiteSupplier 会接收一个 Cookie,并可返回一个 SameSite 值,或返回 nullspring-doc.cadn.net.cn

提供了许多便捷的工厂方法和过滤器方法,可帮助您快速匹配特定的 Cookie。 例如,添加以下 Bean 将自动为所有名称与正则表达式 myapp.* 匹配的 Cookie 设置 SameSiteLaxspring-doc.cadn.net.cn

import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration(proxyBeanMethods = false)
public class MySameSiteConfiguration {

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

}
import org.springframework.boot.web.servlet.server.CookieSameSiteSupplier
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration(proxyBeanMethods = false)
class MySameSiteConfiguration {

	@Bean
	fun applicationCookieSameSiteSupplier(): CookieSameSiteSupplier {
		return CookieSameSiteSupplier.ofLax().whenHasNameMatching("myapp.*")
	}

}

字符编码

可使用 server.servlet.encoding.* 配置属性来配置嵌入式 Servlet 容器在请求和响应处理中的字符编码行为。spring-doc.cadn.net.cn

当请求的 Accept-Language 请求头指明了该请求所用的区域设置(locale)时,Servlet 容器将自动将其映射为相应的字符集(charset)。 每个容器都提供了默认的区域设置到字符集的映射关系,您应验证这些默认映射是否满足您应用程序的需求。 若不满足,请使用 server.servlet.encoding.mapping 配置属性来自定义映射关系,如下例所示:spring-doc.cadn.net.cn

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

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

编程式自定义

如果需要以编程方式配置嵌入式 Servlet 容器,可以注册一个实现 WebServerFactoryCustomizer 接口的 Spring Bean。 WebServerFactoryCustomizer 提供对 ConfigurableServletWebServerFactory 的访问,后者包含大量用于自定义的 setter 方法。 以下示例展示了如何以编程方式设置端口号:spring-doc.cadn.net.cn

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

@Component
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

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

}
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory
import org.springframework.stereotype.Component

@Component
class MyWebServerFactoryCustomizer : WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

	override fun customize(server: ConfigurableServletWebServerFactory) {
		server.setPort(9000)
	}

}

TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactoryConfigurableServletWebServerFactory 的专用变体,分别提供了针对 Tomcat、Jetty 和 Undertow 的额外定制化 setter 方法。 以下示例展示了如何定制 TomcatServletWebServerFactory,以访问 Tomcat 特定的配置选项:spring-doc.cadn.net.cn

import java.time.Duration;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class MyTomcatWebServerFactoryCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

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

}
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.stereotype.Component
import java.time.Duration

@Component
class MyTomcatWebServerFactoryCustomizer : WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

	override fun customize(server: TomcatServletWebServerFactory) {
		server.addConnectorCustomizers({ connector -> connector.asyncTimeout = Duration.ofSeconds(20).toMillis() })
	}

}

直接自定义 ConfigurableServletWebServerFactory

对于需要继承自 ServletWebServerFactory 的更高级使用场景,您可以自行暴露该类型的 Bean。spring-doc.cadn.net.cn

为许多配置选项提供了 Setter 方法。 此外,还提供了若干受保护的方法“钩子”,以便您在需要执行更复杂操作时使用。 详情请参阅 ConfigurableServletWebServerFactory 的 API 文档。spring-doc.cadn.net.cn

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

JSP 局限性

当运行使用嵌入式 Servlet 容器(并打包为可执行归档文件)的 Spring Boot 应用程序时,JSP 支持存在一些限制。spring-doc.cadn.net.cn