此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Boot 3.4.0spring-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”)是一个丰富的“模型视图控制器”Web 框架。 Spring MVC 允许您创建特殊的@Controller@RestControllerbean 来处理传入的 HTTP 请求。 控制器中的方法通过使用@RequestMapping附注。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 Framework 的一部分,详细信息可在参考文档中找到。 spring.io/guides 上还提供了几个涵盖 Spring MVC 的指南。spring-doc.cadn.net.cn

您可以定义任意数量的RouterFunctionbean 来模块化路由器的定义。 如果需要应用优先级,则可以对 bean 进行排序。

Spring MVC 自动配置

Spring Boot 为 Spring MVC 提供了自动配置,适用于大多数应用程序。 它取代了对@EnableWebMvc并且两者不能一起使用。 除了 Spring MVC 的默认值之外,自动配置还提供以下功能:spring-doc.cadn.net.cn

如果你想保留这些 Spring Boot MVC 自定义并进行更多的 MVC 自定义(拦截器、格式化程序、视图控制器和其他功能),你可以添加自己的@Configurationtype 类WebMvcConfigurer但没有 @EnableWebMvc.spring-doc.cadn.net.cn

如果要提供RequestMappingHandlerMapping,RequestMappingHandlerAdapterExceptionHandlerExceptionResolver,并且仍然保留 Spring Boot MVC 自定义,则可以声明一个 bean 类型的WebMvcRegistrations并使用它来提供这些组件的自定义实例。 自定义实例将受 Spring MVC 的进一步初始化和配置的约束。 要参与并在需要时覆盖该后续处理,需要一个WebMvcConfigurer应该使用。spring-doc.cadn.net.cn

如果您不想使用自动配置并希望完全控制 Spring MVC,请添加您自己的@Configuration注解@EnableWebMvc. 或者,添加您自己的@Configuration-注释DelegatingWebMvcConfiguration@EnableWebMvcAPI 文档。spring-doc.cadn.net.cn

Spring MVC 转换服务

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

如果要自定义ConversionService由 Spring MVC 使用,您可以提供WebMvcConfigurer带有addFormatters方法。 通过此方法,您可以注册您喜欢的任何转换器,也可以委托给ApplicationConversionService.spring-doc.cadn.net.cn

转换也可以使用spring.mvc.format.*configuration 属性。 如果未配置,则使用以下默认值: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 的LocalDateTime,OffsetDateTimeZonedDateTimespring-doc.cadn.net.cn

HttpMessage转换器

Spring MVC 使用HttpMessageConverter接口来转换 HTTP 请求和响应。 合理的默认值是开箱即用的。 例如,对象可以自动转换为 JSON(通过使用 Jackson 库)或 XML(通过使用 Jackson XML 扩展,如果可用,或者通过使用 JAXB,如果 Jackson XML 扩展不可用)。 默认情况下,字符串以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)
	}

}

为了进一步控制,您还可以将 sub-classHttpMessageConverters并覆盖其postProcessConverters和/或postProcessPartConverters方法。 当您想要重新排序或删除 Spring MVC 默认配置的某些转换器时,这可能很有用。spring-doc.cadn.net.cn

MessageCodes解析器

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. 它使用ResourceHttpRequestHandler从 Spring MVC,以便您可以通过添加自己的WebMvcConfigurer并覆盖addResourceHandlers方法。spring-doc.cadn.net.cn

在独立的 Web 应用程序中,未启用容器中的默认 Servlet。 可以使用server.servlet.register-default-servlet财产。spring-doc.cadn.net.cn

默认 Servlet 充当后备,从ServletContext如果 Spring 决定不处理它。 大多数情况下,这不会发生(除非你修改了默认的 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/**如果 jar 文件以 Webjars 格式打包,则从 jar 文件中提供。 可以使用spring.mvc.webjars-path-pattern财产。spring-doc.cadn.net.cn

请勿使用src/main/webapp目录(如果您的应用程序打包为 jar)。 虽然这个目录是一个通用标准,但它仅适用于 war 打包,如果你生成一个 jar,大多数构建工具都会默默地忽略它。

Spring Boot 还支持 Spring MVC 提供的高级资源处理功能,允许使用诸如缓存清除静态资源或对 Webjar 使用与版本无关的 URL 等用例。spring-doc.cadn.net.cn

要对 Webjar 使用与版本无关的 URL,请添加webjars-locator-coreDependency。 然后声明您的 Webjar。 以 jQuery 为例,添加"/webjars/jquery/jquery.min.js"结果"/webjars/jquery/x.y.z/jquery.min.js"哪里x.y.z是 Webjar 版本。spring-doc.cadn.net.cn

如果您使用 JBoss,则需要声明webjars-locator-jboss-vfsdependency 而不是webjars-locator-core. 否则,所有 Webjar 都会解析为404.

要使用缓存清除,以下配置为所有静态资源配置缓存清除解决方案,从而有效地添加内容哈希,例如<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>,在 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:
          content:
            enabled: true
            paths: "/**"
指向资源的链接在运行时在模板中重写,这要归功于ResourceUrlEncodingFilter这是为 Thymeleaf 和 FreeMarker 自动配置的。 使用 JSP 时,应手动声明此过滤器。 其他模板引擎目前不自动支持,但可以使用自定义模板宏/帮助程序并使用ResourceUrlProvider.

例如,使用 JavaScript 模块加载器动态加载资源时,重命名文件不是一个选项。 这就是为什么还支持其他策略并且可以组合的原因。 “fixed” 策略在 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/"使用固定版本控制策略 ("/v12/js/lib/mymodule.js"),而其他资源仍然使用内容 (<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>).spring-doc.cadn.net.cn

WebProperties.Resources以获取更多支持的选项。spring-doc.cadn.net.cn

此功能已在专门的博客文章和 Spring Framework 的参考文档中进行了详细描述。spring-doc.cadn.net.cn

欢迎页面

Spring Boot 支持静态和模板化欢迎页面。 它首先查找index.html文件。 如果未找到,则查找index模板。 如果找到任何一个,它将自动用作应用程序的欢迎页面。spring-doc.cadn.net.cn

这仅充当应用程序定义的实际索引路由的回退。 排序由HandlerMappingbeans 的 bean 中,默认情况下,它的作用如下:spring-doc.cadn.net.cn

RouterFunctionMappingspring-doc.cadn.net.cn

使用RouterFunctionspring-doc.cadn.net.cn

RequestMappingHandlerMappingspring-doc.cadn.net.cn

@Controllerspring-doc.cadn.net.cn

WelcomePageHandlerMappingspring-doc.cadn.net.cn

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

定制网站图标

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

路径匹配和内容协商

Spring MVC 可以通过查看请求路径并将其与应用程序中定义的 Map(例如,@GetMappingController 方法上的注释)。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

还有其他方法可以处理 HTTP 客户端,这些客户端不能始终如一地发送正确的 “Accept” 请求标头。 我们可以使用 query 参数来确保像"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-strategyconfiguration 属性,如以下示例所示: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

ConfigurableWebBindingInitializer

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 容器一起使用时,存在几个已知的限制

当您使用具有默认配置的模板引擎之一时,您的模板会自动从src/main/resources/templates.spring-doc.cadn.net.cn

根据您运行应用程序的方式,IDE 可能会以不同的方式对 Classpath 进行排序。 在 IDE 中,从 main 方法运行应用程序时,与使用 Maven 或 Gradle 或从其打包的 jar 运行应用程序时的顺序不同。 这可能会导致 Spring Boot 无法找到预期的模板。 如果遇到此问题,可以在 IDE 中对 Classpath 重新排序,以便将模块的类和资源放在最前面。

错误处理

默认情况下, Spring Boot 提供了一个/error映射,它以合理的方式处理所有错误,并在 servlet 容器中注册为“全局”错误页。 对于计算机客户端,它会生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。 对于浏览器客户端,有一个 “whitelabel” 错误视图,它以 HTML 格式呈现相同的数据(要对其进行自定义,请添加View解析为error).spring-doc.cadn.net.cn

有许多server.error属性,如果要自定义默认错误处理行为,则可以设置这些属性。 请参阅附录的 Server Properties 部分。spring-doc.cadn.net.cn

要完全替换默认行为,您可以实现ErrorController并注册该类型的 bean 定义或添加ErrorAttributes以使用现有机制,但替换内容。spring-doc.cadn.net.cn

BasicErrorController可用作自定义ErrorController. 如果要为新的内容类型添加处理程序(默认为 handletext/html具体来说,并为其他所有内容提供回退)。 为此,请扩展BasicErrorController中,添加一个带有@RequestMapping具有produces属性,并创建一个新类型的 bean。

从 Spring Framework 6.0 开始,支持 RFC 9457 问题详细信息。 Spring MVC 可以使用application/problem+jsonmedia 类型,例如: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.enabledtrue.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
	}

}

在前面的示例中,如果MyException由定义在SomeController,则MyErrorBody使用 POJO 而不是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>

要映射所有5xx错误,则您的目录结构将如下所示:spring-doc.cadn.net.cn

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

对于更复杂的映射,您还可以添加实现ErrorViewResolver接口,如以下示例所示: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方法@ControllerAdvice. 这ErrorController然后选取任何未经处理的异常。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必须显式注册为ERRORDispatcher,如以下示例所示:
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不包括ERRORDispatcher 类型。spring-doc.cadn.net.cn

WAR 部署中的错误处理

当部署到 servlet 容器时, Spring Boot 使用其错误页面过滤器将具有错误状态的请求转发到相应的错误页面。 这是必需的,因为 servlet 规范没有提供用于注册错误页面的 API。 根据要将 war 文件部署到的容器和应用程序使用的技术,可能需要一些额外的配置。spring-doc.cadn.net.cn

如果尚未提交响应,则错误页面过滤器只能将请求转发到正确的错误页面。 默认情况下,WebSphere Application Server 8.0 及更高版本在成功完成 servlet 的服务方法后提交响应。 您应该通过设置com.ibm.ws.webcontainer.invokeFlushAfterServicefalse.spring-doc.cadn.net.cn

CORS 支持

跨域资源共享 (CORS) 是大多数浏览器实现的 W3C 规范,它允许您以灵活的方式指定授权的跨域请求类型,而不是使用一些安全性较低且功能较弱的方法,例如 IFRAME 或 JSONP。spring-doc.cadn.net.cn

从版本 4.2 开始,Spring MVC 支持 CORS。 使用控制器方法 CORS 配置@CrossOriginSpring Boot 应用程序中的注释不需要任何特定的配置。全局 CORS 配置可以通过注册WebMvcConfigurerbean 替换为自定义的addCorsMappings(CorsRegistry)方法,如以下示例所示: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

如果您更喜欢 REST 端点的 JAX-RS 编程模型,则可以使用其中一个可用的实现,而不是 Spring MVC。JerseyApache CXF 开箱即用。 CXF 要求您注册其ServletFilter作为@Bean在您的应用程序上下文中。 Jersey 有一些原生的 Spring 支持,因此我们还在 Spring Boot 中提供了对它的自动配置支持,以及一个Starters。spring-doc.cadn.net.cn

要开始使用 Jersey,请包括spring-boot-starter-jersey作为依赖项,然后您需要一个@Bean的类型ResourceConfig,您可以在其中注册所有终端节点,如以下示例所示: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 对扫描可执行档案的支持相当有限。 例如,它无法扫描在完全可执行的 jar 文件WEB-INF/classes运行可执行 war 文件时。 为避免此限制,packages方法,并且应使用register方法,如前面的示例所示。

对于更高级的自定义,您还可以注册任意数量的 bean 来实现ResourceConfigCustomizer.spring-doc.cadn.net.cn

所有已注册的端点都应该是一个@Component使用 HTTP 资源注释 (@GET和其他),如以下示例所示: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 已注册并映射到 。 您可以通过添加/*@ApplicationPath发送到您的ResourceConfig.spring-doc.cadn.net.cn

默认情况下,Jersey 在@Bean的类型ServletRegistrationBeanjerseyServletRegistration. 默认情况下,Servlet 是延迟初始化的,但您可以通过设置spring.jersey.servlet.load-on-startup. 您可以通过创建一个具有相同名称的 bean 来禁用或覆盖该 bean。 您还可以使用过滤器而不是 servlet,方法是将spring.jersey.type=filter(在这种情况下,@Bean替换或覆盖是jerseyFilterRegistration). 该过滤器具有@Order,您可以使用spring.jersey.filter.order. 当使用 Jersey 作为过滤器时,必须存在一个 Servlet,该 Servlet 将处理 Jersey 未拦截的任何请求。 如果您的应用程序不包含这样的 Servlet,则可能需要通过设置server.servlet.register-default-servlettrue. Servlet 和过滤器注册都可以通过使用spring.jersey.init.*以指定属性的映射。spring-doc.cadn.net.cn

嵌入式 Servlet 容器支持

对于 servlet 应用程序,Spring Boot 包括对嵌入式 TomcatJettyUndertow 服务器的支持。 大多数开发人员使用适当的 starter 来获取完全配置的实例。 默认情况下,嵌入式服务器在端口8080.spring-doc.cadn.net.cn

Servlet、过滤器和侦听器

使用嵌入式 servlet 容器时,您可以注册 servlet、过滤器和所有侦听器(例如HttpSessionListener),通过使用 Spring Bean 或扫描 servlet 组件。spring-doc.cadn.net.cn

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

任何Servlet,Filter或 Servlet*Listener实例,即 Spring Bean 注册到嵌入式容器中。 如果您想引用application.properties在配置期间。spring-doc.cadn.net.cn

默认情况下,如果上下文仅包含单个 Servlet,则会将其映射到 。 在多个 servlet bean 的情况下,bean 名称用作路径前缀。 筛选器映射到 。//*spring-doc.cadn.net.cn

如果基于约定的映射不够灵活,您可以使用ServletRegistrationBean,FilterRegistrationBeanServletListenerRegistrationBean类以实现完全控制。spring-doc.cadn.net.cn

通常,将 filter bean 保持无序状态是安全的。 如果需要特定顺序,则应注释Filter@Order或使其实现Ordered. 您无法配置Filter通过使用@Order. 如果无法更改Filter类来添加@Order或实施Ordered中,您必须定义一个FilterRegistrationBean对于Filter并使用setOrder(int)方法。 避免配置一个过滤器,该过滤器在Ordered.HIGHEST_PRECEDENCE,因为它可能违背应用程序的字符编码配置。 如果 servlet 过滤器包装了请求,则应将其配置为小于或等于OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER.spring-doc.cadn.net.cn

要查看每个Filter在您的应用程序中,为web 日志记录组 (logging.level.web=debug). 然后,将在启动时记录已注册过滤器的详细信息,包括它们的顺序和 URL 模式。
注册时要小心Filterbean,因为它们在应用程序生命周期的早期就被初始化了。 如果您需要注册Filter,请考虑使用DelegatingFilterProxyRegistrationBean相反。

Servlet 上下文初始化

嵌入式 servlet 容器不会直接执行ServletContainerInitializerinterface 或 Spring 的WebApplicationInitializer接口。 这是一个有意的设计决策,旨在降低设计为在 war 中运行的第三方库可能会破坏 Spring Boot 应用程序的风险。spring-doc.cadn.net.cn

如果需要在 Spring Boot 应用程序中执行 servlet 上下文初始化,则应注册一个实现ServletContextInitializer接口。 单onStartupmethod 提供对ServletContext并且,如有必要,可以很容易地用作现有WebApplicationInitializer.spring-doc.cadn.net.cn

扫描 Servlet、Filter 和侦听器

使用嵌入式容器时,自动注册带有@WebServlet,@WebFilter@WebListener可以通过使用@ServletComponentScan.spring-doc.cadn.net.cn

@ServletComponentScan在独立容器中不起作用,其中使用容器的内置发现机制。

The ServletWebServerApplicationContext

在后台,Spring Boot 使用不同类型的ApplicationContext,了解嵌入式 servlet 容器支持。 这ServletWebServerApplicationContext是一种特殊类型的WebApplicationContext它通过搜索单个ServletWebServerFactory豆。 通常为TomcatServletWebServerFactory,JettyServletWebServerFactoryUndertowServletWebServerFactory已自动配置。spring-doc.cadn.net.cn

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

在嵌入式容器设置中,ServletContext设置为服务器启动的一部分,该启动发生在应用程序上下文初始化期间。 由于这个 bean 在ApplicationContext不能使用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 容器

可以使用 Spring 配置常见的 servlet 容器设置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

SameSiteWeb 浏览器可以使用 cookie 属性来控制是否以及如何在跨站点请求中提交 cookie。 该属性与现代 Web 浏览器尤其相关,这些浏览器已开始更改在缺少该属性时使用的默认值。spring-doc.cadn.net.cn

如果要更改SameSite属性,您可以使用server.servlet.session.cookie.same-site财产。 自动配置的 Tomcat、Jetty 和 Undertow 服务器支持此属性。 它还用于配置基于 Spring Session servletSessionRepository豆。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"

如果要更改SameSite添加到其他 Cookie 的属性HttpServletResponse,您可以使用CookieSameSiteSupplier. 这CookieSameSiteSupplier将传递一个Cookie,并且可能会返回SameSitevalue 或null.spring-doc.cadn.net.cn

您可以使用许多方便的 Factory 和 Filter 方法来快速匹配特定的 Cookie。 例如,添加以下 bean 将自动应用SameSiteLax对于名称与正则表达式匹配的所有 Cookiemyapp.*.spring-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.*")
	}

}

字符编码

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

当请求的Accept-Languageheader 表示请求的语言环境,它将被 servlet 容器自动映射到字符集。 每个容器都提供 charset 映射的默认区域设置,您应该验证它们是否满足应用程序的需要。 如果它们不这样做,请使用server.servlet.encoding.mappingconfiguration 属性来自定义映射,如以下示例所示:spring-doc.cadn.net.cn

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

在前面的示例中,ko(韩语) locale 已映射到UTF-8. 这相当于<locale-encoding-mapping-list>条目web.xml文件。spring-doc.cadn.net.cn

编程自定义

如果需要以编程方式配置嵌入式 servlet 容器,可以注册一个实现WebServerFactoryCustomizer接口。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)
	}

}

TomcatServletWebServerFactory,JettyServletWebServerFactoryUndertowServletWebServerFactory是 的专用变体ConfigurableServletWebServerFactory分别具有 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。 如果你需要做一些更奇特的事情,还提供了几个受保护的方法 “钩子”。 请参阅ConfigurableServletWebServerFactoryAPI 文档了解详细信息。spring-doc.cadn.net.cn

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

JSP 限制

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