此版本仍在开发中,尚未视为稳定版。如需最新稳定版本,请使用 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 框架”

以下代码展示了一个典型的用于提供 JSON 数据的 @RestControllerspring-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,并使用它来提供这些组件的自定义实例。 自定义实例将由 Spring MVC 进行进一步的初始化和配置。 若要参与并在需要时覆盖该后续处理过程,应使用 WebMvcConfigurerspring-doc.cadn.net.cn

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

Spring MVC 转换服务

Spring MVC 使用了一种不同的 ConversionService,与用于从您的 application.propertiesapplication.yaml 文件转换值的机制不同。 这意味着 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

<property> </property> 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

HttpMessageConverter

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

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

如果您需要添加或自定义转换器,可以声明一个或多个 ClientHttpMessageConvertersCustomizerServerHttpMessageConvertersCustomizer 作为 Bean。在那里,您可以选择是将转换器实例添加到默认转换器之前(addCustomConverter),还是覆盖特定的默认转换器(如 withJsonConverter)。spring-doc.cadn.net.cn

请参见以下列表中的示例:spring-doc.cadn.net.cn

import java.text.SimpleDateFormat;

import tools.jackson.databind.json.JsonMapper;

import org.springframework.boot.http.converter.autoconfigure.ClientHttpMessageConvertersCustomizer;
import org.springframework.boot.http.converter.autoconfigure.ServerHttpMessageConvertersCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverters.ClientBuilder;
import org.springframework.http.converter.HttpMessageConverters.ServerBuilder;
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;

@Configuration(proxyBeanMethods = false)
public class MyHttpMessageConvertersConfiguration {

	@Bean
	public ClientHttpMessageConvertersCustomizer myClientConvertersCustomizer() {
		return (clientBuilder) -> clientBuilder.addCustomConverter(new AdditionalHttpMessageConverter())
			.addCustomConverter(new AnotherHttpMessageConverter());
	}

	@Bean
	public JacksonConverterCustomizer jacksonConverterCustomizer() {
		JsonMapper jsonMapper = JsonMapper.builder().defaultDateFormat(new SimpleDateFormat("yyyy-MM")).build();
		return new JacksonConverterCustomizer(jsonMapper);
	}

	// contribute a custom JSON converter to both client and server
	static class JacksonConverterCustomizer
			implements ClientHttpMessageConvertersCustomizer, ServerHttpMessageConvertersCustomizer {

		private final JsonMapper jsonMapper;

		JacksonConverterCustomizer(JsonMapper jsonMapper) {
			this.jsonMapper = jsonMapper;
		}

		@Override
		public void customize(ClientBuilder builder) {
			builder.withJsonConverter(new JacksonJsonHttpMessageConverter(this.jsonMapper));
		}

		@Override
		public void customize(ServerBuilder builder) {
			builder.withJsonConverter(new JacksonJsonHttpMessageConverter(this.jsonMapper));
		}

	}

}
import org.springframework.boot.http.converter.autoconfigure.ClientHttpMessageConvertersCustomizer
import org.springframework.boot.http.converter.autoconfigure.ServerHttpMessageConvertersCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.HttpMessageConverters
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter
import tools.jackson.databind.json.JsonMapper
import java.text.SimpleDateFormat

@Configuration(proxyBeanMethods = false)
class MyHttpMessageConvertersConfiguration {

	@Bean
	fun myClientConvertersCustomizer(): ClientHttpMessageConvertersCustomizer {
		return ClientHttpMessageConvertersCustomizer { clientBuilder: HttpMessageConverters.ClientBuilder ->
			clientBuilder
				.addCustomConverter(AdditionalHttpMessageConverter())
				.addCustomConverter(AnotherHttpMessageConverter())
		}
	}

	@Bean
	fun jacksonConverterCustomizer(): JacksonConverterCustomizer {
		val jsonMapper = JsonMapper.builder()
			.defaultDateFormat(SimpleDateFormat("yyyy-MM"))
			.build()
		return JacksonConverterCustomizer(jsonMapper)
	}

	// contribute a custom JSON converter to both client and server
	class JacksonConverterCustomizer(private val jsonMapper: JsonMapper) :
		ClientHttpMessageConvertersCustomizer, ServerHttpMessageConvertersCustomizer {

		override fun customize(builder: HttpMessageConverters.ClientBuilder) {
			builder.withJsonConverter(JacksonJsonHttpMessageConverter(this.jsonMapper))
		}

		override fun customize(builder: HttpMessageConverters.ServerBuilder) {
			builder.withJsonConverter(JacksonJsonHttpMessageConverter(this.jsonMapper))
		}
	}

}

MessageCodesResolver

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属性进行启用。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

不要使用src/main/webapp目录,如果您的应用程序被打包为jar文件。 虽然该目录是一个常见的标准,但它仅在war打包时有效,并且如果您生成了jar文件,则大多数构建工具会默默地忽略它。

Spring Boot 也支持由 Spring MVC 提供的高级资源处理功能,允许使用诸如缓存刷新静态资源或为 Webjars 使用版本无关 URL 等用例。spring-doc.cadn.net.cn

要使用版本无关的URL来引用Webjars,请添加依赖项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

要使用缓存破坏技术,以下配置为所有静态资源设置了缓存破坏解决方案,有效添加了内容哈希,例如 <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: "/**"
由于为 Thymeleaf 和 FreeMarker 自动配置了 ResourceUrlEncodingFilter,资源链接会在运行时于模板中被重写。 使用 JSP 时,您应手动声明此过滤器。 其他模板引擎目前不支持自动配置,但可以通过自定义模板宏/辅助函数并使用 ResourceUrlProvider 来实现支持。

当使用JavaScript模块加载器等方法动态加载资源时,重命名文件并不是一个可行的选择。 因此,也需要支持其他策略,并且这些策略可以组合使用。 一种“固定”的策略是在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"

spring-doc.cadn.net.cn

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

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

自定义网站图标(Favicon)

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

路径匹配与内容协商

Spring MVC 可以通过查看请求路径并将其与应用程序中定义的映射(例如,Controller 方法上的 @GetMapping 注解)进行匹配,将传入的 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-mappings 设置为 false 以完全禁用静态内容的服务。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和JSPs。 此外,许多其他模板引擎也包含了自己的Spring MVC集成。spring-doc.cadn.net.cn

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

如果可能,应避免使用JSPs。 在与嵌入式Servlet容器一起使用时,它们存在若干已知限制

当您使用这些模板引擎并以默认配置使用时,您的模板会自动从src/main/resources/templates中被识别。spring-doc.cadn.net.cn

根据您运行应用程序的方式,IDE可能会以不同的顺序排列类路径。 从主方法在IDE中运行应用程序与使用Maven或Gradle或打包的jar文件运行应用程序会得到不同的排序结果。 这可能导致Spring Boot无法找到预期的模板。 如果您遇到此问题,请在IDE中重新排序类路径,将模块的类和资源放在首位。

错误处理

默认情况下,Spring Boot 提供一个 /error 映射,以合理的方式处理所有错误,并将其注册为 Servlet 容器中的“全局”错误页面。 对于机器客户端,它会生成一个包含错误详情、HTTP 状态和异常消息的 JSON 响应。 对于浏览器客户端,会有一个spring-doc.cadn.net.cn

如果你想要自定义默认的错误处理行为,可以设置多个 spring.web.error 属性。 参见附录中的Web 属性部分。spring-doc.cadn.net.cn

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

BasicErrorController 可用作自定义 ErrorController 的基类。 如果您想为新内容类型添加处理器,这一点尤其有用(默认情况下仅专门处理 text/html,并为其他所有内容提供后备方案)。 为此,请扩展 BasicErrorController,添加一个公共方法,该方法带有具备 produces 属性的 @RequestMapping,并创建您新类型的 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.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
	}

}

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

在某些情况下,控制器层面处理的错误不会被 Web 观测(observations)或指标基础设施记录。 应用程序可以通过在观测上下文中设置已处理的异常,确保此类异常被观测记录下来。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.webmvc.autoconfigure.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.webmvc.autoconfigure.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.error.ErrorPage;
import org.springframework.boot.web.error.ErrorPageRegistrar;
import org.springframework.boot.web.error.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.error.ErrorPage
import org.springframework.boot.web.error.ErrorPageRegistrar
import org.springframework.boot.web.error.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"))
	}

}
如果您注册了一个路径最终由 Filter 处理的 ErrorPage(这在某些非 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 使用其错误页面过滤器将带有错误状态的请求转发到相应的错误页面。 这很必要,因为Servlet规范并未提供注册错误页面的API。 根据您要部署WAR文件的容器类型以及您的应用使用的技术,可能需要一些额外的配置。spring-doc.cadn.net.cn

The error page filter can only forward the request to the correct error page if the response has not already been committed. By default, WebSphere Application Server 8.0 and later commits the response upon successful completion of a servlet’s service method. You should disable this behavior by setting com.ibm.ws.webcontainer.invokeFlushAfterService to false.spring-doc.cadn.net.cn

CORS 支持

跨域资源共享(CORS)是一种由W3C 规范定义、并被大多数浏览器所实现的机制,它允许你以灵活的方式指定哪些跨域请求是被授权的,从而替代一些安全性较低且功能较弱的方法,例如 IFRAME 或 JSONP。spring-doc.cadn.net.cn

自 4.2 版本起,Spring MVC 支持 CORS。 在您的 Spring Boot 应用程序中,使用带有 @CrossOrigin 注解的 控制器方法 CORS 配置 无需任何特定配置。 可以通过注册一个具有自定义 addCorsMappings(CorsRegistry) 方法的 WebMvcConfigurer Bean 来定义 全局 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/**")
			}
		}
	}

}

API 版本控制

Spring MVC 支持 API 版本控制,可用于随着时间推移逐步演进 HTTP API。 同一个 @Controller 路径可以被多次映射,以支持不同版本的 API。spring-doc.cadn.net.cn

一旦添加了映射,您还需要额外配置 Spring MVC,使其能够使用随请求发送的任何版本信息。 通常,版本信息会通过 HTTP 头、查询参数或作为路径的一部分进行传递。spring-doc.cadn.net.cn

要配置 Spring MVC,您可以使用 WebMvcConfigurer Bean 并重写 configureApiVersioning(…​) 方法,或者使用属性进行配置。spring-doc.cadn.net.cn

例如,以下代码将使用 X-Version HTTP 头来获取版本信息,并在未发送该头时默认使用 1.0.0spring-doc.cadn.net.cn

spring.mvc.apiversion.default=1.0.0
spring.mvc.apiversion.use.header=X-Version
spring:
  mvc:
    apiversion:
      default: 1.0.0
      use:
        header: X-Version

为了获得更完整的控制,您还可以定义 ApiVersionResolverApiVersionParserApiVersionDeprecationHandler Bean,它们将被注入到自动配置的 Spring MVC 配置中。spring-doc.cadn.net.cn

WebClientRestClient 均支持 API 版本控制。 详情请参阅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 作为依赖项引入,然后您需要一个类型为 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 被配置为一个类型为 ServletRegistrationBean@Bean 中的 Servlet,其名称为 jerseyServletRegistration。默认情况下,Servlet 是延迟初始化的,但您可以通过设置 spring.jersey.servlet.load-on-startup 来自定义该行为。你可以通过创建一个同名的 bean 来禁用或覆盖该 bean。您也可以通过设置 spring.jersey.type=filter 来使用过滤器而非 Servlet(此时,要替换或覆盖的 @BeanjerseyFilterRegistration)。该过滤器有一个 @Order,您可以使用 spring.jersey.filter.order 进行设置。当将 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 包含对内嵌 TomcatJetty 服务器的支持。 大多数开发者使用相应的 starter 来获取一个完全配置好的实例。 默认情况下,内嵌服务器在端口 8080 上监听 HTTP 请求。spring-doc.cadn.net.cn

Servlet、过滤器和监听器

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

将 Servlet、Filter 和 Listener 注册为 Spring Bean

任何作为 Spring Bean 的 ServletFilter 或 Servlet *Listener 实例,都会注册到嵌入式容器中。 如果您想在配置期间引用 application.properties 中的值,这会特别方便。spring-doc.cadn.net.cn

默认情况下,如果上下文中只有一个Servlet,则该Servlet映射到/。 在多个Servlet Bean的情况下,Bean名称用作路径前缀。 Filters映射到/*.spring-doc.cadn.net.cn

如果基于约定的映射不够灵活,您可以使用 ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean 类以获得完全的控制权。 如果您更喜欢注解而不是 ServletRegistrationBeanFilterRegistrationBean,您也可以使用 @ServletRegistration@FilterRegistration 作为替代方案。spring-doc.cadn.net.cn

通常情况下,让过滤器 Bean 保持无序是安全的。如果需要特定的顺序,您应该使用 Filter 注解 @Order,或使其实现 Ordered。您无法通过用 @Order 注解其 Bean 方法来配置 Filter 的顺序。如果您无法修改 Filter 类以添加 @Order 或实现 Ordered,则必须为 Filter 定义一个 FilterRegistrationBean,并使用 setOrder(int) 方法设置注册 Bean 的顺序。或者,如果您更喜欢注解,也可以使用 @FilterRegistration 并设置 order 属性。避免在 Ordered.HIGHEST_PRECEDENCE 处配置读取请求体的过滤器,因为它可能与应用程序的字符编码配置相冲突。如果 Servlet 过滤器包装了请求,则应将其配置为小于或等于 OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER 的顺序。spring-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 的访问,并且在必要时可以轻松地用作现有 WebApplicationInitializer 的适配器。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、过滤器和监听器

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

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

ServletWebServerApplicationContext

在底层,Spring Boot 使用了一种不同类型的 ApplicationContext 来支持嵌入式 Servlet 容器。 ServletWebServerApplicationContext 是一种特殊类型的 WebApplicationContext,它通过搜索单个 ServletWebServerFactory Bean 来自我引导。 通常,TomcatServletWebServerFactoryJettyServletWebServerFactory 已被自动配置。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.tomcat)。 例如,可以使用内嵌 Servlet 容器的特定功能来配置访问日志spring-doc.cadn.net.cn

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

SameSite Cookie

The SameSite cookie 属性可以被网页浏览器用来控制在跨站请求中是否提交以及如何提交 cookies。 该属性特别适用于现代网页浏览器,这些浏览器已经开始改变当此属性缺失时使用的默认值。spring-doc.cadn.net.cn

如果您想更改会话 cookie 的 SameSite 属性,可以使用 server.servlet.session.cookie.same-site 属性。 此属性由自动配置的 Tomcat 和 Jetty 服务器支持。 它还用于配置基于 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将自动为所有名称匹配正则表达式SameSite的所有Cookie应用一个Lax属性为myapp.*spring-doc.cadn.net.cn

import org.springframework.boot.web.server.servlet.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.server.servlet.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.*配置属性进行配置。spring-doc.cadn.net.cn

当请求的Accept-Language头指示了请求的语言环境时,Servlet容器会自动将该语言环境映射到一个字符集。 每个容器都提供了默认的语言环境到字符集的映射,并且你应该验证这些映射是否满足你应用的需求。 如果它们不满足需求,可以使用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部署的<locale-encoding-mapping-list>文件中的一个条目。spring-doc.cadn.net.cn

编程式自定义

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

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.server.servlet.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.servlet.ConfigurableServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.stereotype.Component

@Component
class MyWebServerFactoryCustomizer : WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

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

}

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

import java.time.Duration;

import org.springframework.boot.tomcat.servlet.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.server.WebServerFactoryCustomizer
import org.springframework.boot.tomcat.servlet.TomcatServletWebServerFactory
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

  • 使用 Jetty 和 Tomcat,如果你使用 war 打包方式,应该可以正常工作。 可执行的 war 文件可以在启动时通过 java -jar 运行,并且也可以部署到任何标准容器中。 当使用可执行 jar 时,JSP 不受支持。spring-doc.cadn.net.cn

  • 创建自定义error.jsp页面不会覆盖默认的错误处理视图。自定义错误页面应被用于替代。spring-doc.cadn.net.cn

  • 如果你使用 mvn spring-boot:rungradle bootRun 运行你的应用程序,并且你偏离了标准的 src/main/webapp 目录结构,那么你可能需要设置一个 WAR_SOURCE_DIRECTORY 环境变量,以便 Spring Boot 能够找到你的 JSP 文件。spring-doc.cadn.net.cn