|
此版本仍在开发中,尚未被视为稳定版本。如需最新稳定版本,请使用 Spring Boot 4.0.4! |
Servlet Web 应用程序
如果要构建基于 Servlet 的 Web 应用程序,您可以利用 Spring Boot 对 Spring MVC 或 Jersey 的自动配置功能。
“Spring Web MVC 框架”
Spring Web MVC 框架(通常简称为“Spring MVC”)是一个功能丰富的“模型-视图-控制器”(MVC)Web 框架。
Spring MVC 允许您创建特殊的 @Controller 或 @RestController Bean 来处理传入的 HTTP 请求。
您控制器中的方法通过使用 @RequestMapping 注解映射到 HTTP 请求。
以下代码展示了一个典型的 @RestController,用于提供 JSON 数据:
-
Java
-
Kotlin
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”(函数式变体)将路由配置与请求的实际处理分离开来,如下例所示:
-
Java
-
Kotlin
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)
}
}
-
Java
-
Kotlin
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 的指南。
您可以定义任意多个 RouterFunction Bean,以模块化方式定义路由器。
如果需要指定优先级顺序,可以对 Bean 进行排序。 |
Spring MVC 自动配置
Spring Boot 为 Spring MVC 提供了自动配置功能,该功能可与大多数应用程序良好协作。
它取代了对 @EnableWebMvc 的需求,且两者不可同时使用。
除 Spring MVC 的默认配置外,该自动配置还提供以下功能:
-
包含
ContentNegotiatingViewResolver和BeanNameViewResolverBean。 -
支持提供静态资源,包括对 WebJars 的支持(在本文档的后续部分中介绍)。
-
自动注册
Converter、GenericConverter和FormatterBean。 -
支持
HttpMessageConverters(相关内容将在本文档后续部分介绍)。 -
MessageCodesResolver的自动注册(相关内容将在本文档后续部分介绍)。 -
静态
index.html支持。 -
自动使用一个
ConfigurableWebBindingInitializer类型的 Bean(相关内容将在本文档后续部分介绍)。
如果您希望保留这些 Spring Boot MVC 的自定义配置,并进行更多 MVC 自定义配置(例如拦截器、格式化器、视图控制器及其他功能),您可以添加一个类型为 WebMvcConfigurer 的自定义 @Configuration 类,但不能包含 @EnableWebMvc。
如果您希望提供 RequestMappingHandlerMapping、RequestMappingHandlerAdapter 或 ExceptionHandlerExceptionResolver 的自定义实例,同时仍保留 Spring Boot MVC 的默认定制功能,则可以声明一个类型为 WebMvcRegistrations 的 Bean,并利用该 Bean 提供这些组件的自定义实例。
这些自定义实例将由 Spring MVC 进行进一步的初始化和配置。
如需参与该后续处理过程,或按需覆盖该处理逻辑,则应使用 WebMvcConfigurer。
如果您不想使用自动配置,而希望完全掌控 Spring MVC,请添加您自己的、使用 @Configuration 注解的类。
或者,按照 @EnableWebMvc API 文档中的说明,添加您自己的、使用 @Configuration 注解的 DelegatingWebMvcConfiguration 类。
Spring MVC 转换服务
Spring MVC 使用的 ConversionService 与用于从您的 application.properties 或 application.yaml 文件中转换值的 ConversionService 不同。
这意味着 Period、Duration 和 DataSize 转换器不可用,且 @DurationUnit 与 @DataSizeUnit 注解将被忽略。
如果您希望自定义 Spring MVC 所使用的 ConversionService,可以提供一个带有 addFormatters 方法的 WebMvcConfigurer Bean。
通过该方法,您可以注册任意所需的转换器,或者委托给 ApplicationConversionService 上提供的静态方法。
还可以通过 spring.mvc.format.* 配置属性来自定义转换。
若未进行配置,则使用以下默认值:
| 属性 | DateTimeFormatter |
格式 |
|---|---|---|
|
|
|
|
|
java.time 的 |
|
|
java.time 的 |
HTTP消息转换器
Spring MVC 使用 HttpMessageConverter 接口来转换 HTTP 请求和响应。
开箱即用,已包含合理的默认配置。
例如,对象可自动转换为 JSON(通过使用 Jackson 库)或 XML(若 Jackson XML 扩展可用,则使用该扩展;否则使用 JAXB)。
默认情况下,字符串采用 UTF-8 编码。
上下文中存在的任意 HttpMessageConverter Bean 都会被添加到转换器列表中。
您也可以以相同方式覆盖默认转换器。
如果您需要添加或自定义转换器,可以声明一个或多个 ClientHttpMessageConvertersCustomizer 或
ServerHttpMessageConvertersCustomizer 作为 Bean。在那里,您可以选择是否将转换器实例添加到默认实例之前(addCustomConverter),或者是否要覆盖特定的默认转换器(如 withJsonConverter)。
查看以下清单以获取示例:
-
Java
-
Kotlin
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))
}
}
}
消息代码解析器
Spring MVC 提供了一种策略,用于根据绑定错误生成渲染错误消息所需的错误代码:MessageCodesResolver。
如果您设置了 spring.mvc.message-codes-resolver-format 属性 PREFIX_ERROR_CODE 或 POSTFIX_ERROR_CODE,Spring Boot 会为您自动创建一个(参见 DefaultMessageCodesResolver.Format 中的枚举)。
静态内容
默认情况下,Spring Boot 从类路径中名为 /static(或 /public、/resources、/META-INF/resources)的目录,或从 ServletContext 的根目录提供静态内容。
它使用 Spring MVC 中的 ResourceHttpRequestHandler,因此您可以通过添加自己的 WebMvcConfigurer 并重写 addResourceHandlers 方法来自定义该行为。
在独立的 Web 应用程序中,容器的默认 Servlet 未启用。
可通过 server.servlet.register-default-servlet 属性启用该默认 Servlet。
默认 Servlet 充当后备机制,当 Spring 决定不处理某个请求时,它将从 ServletContext 的根目录提供内容。
大多数情况下,这种情况不会发生(除非您修改了默认的 MVC 配置),因为 Spring 始终可以通过 DispatcherServlet 处理请求。
默认情况下,资源映射到 /**,但您可以通过 spring.mvc.static-path-pattern 属性来调整该设置。
例如,将所有资源重新定位到 /resources/** 可按如下方式实现:
-
Properties
-
YAML
spring.mvc.static-path-pattern=/resources/**
spring:
mvc:
static-path-pattern: "/resources/**"
您还可以通过使用 spring.web.resources.static-locations 属性来自定义静态资源位置(以目录路径列表替换默认值)。
根 Servlet 上下文路径 "/" 也会自动被添加为一个位置。
除了前述“标准”静态资源位置外,Webjars 内容属于一种特殊情况。
默认情况下,任何路径为 /webjars/** 的资源,若以 Webjars 格式打包在 JAR 文件中,则将直接从该 JAR 文件中提供服务。
该路径可通过 spring.mvc.webjars-path-pattern 属性进行自定义。
如果您的应用程序被打包为 JAR 文件,请勿使用 src/main/webapp 目录。
尽管该目录是一种常见标准,但它**仅**适用于 WAR 打包方式;当您生成 JAR 文件时,大多数构建工具会静默忽略该目录。 |
Spring Boot 还支持 Spring MVC 提供的高级资源处理功能,可用于实现静态资源的缓存失效(cache-busting)或为 Webjars 使用与版本无关的 URL 等场景。
若要为 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 的版本号。
要使用缓存清除(cache busting),以下配置为所有静态资源设置了一个缓存清除解决方案,从而在 URL 中有效添加内容哈希(例如 <link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>):
-
Properties
-
YAML
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 中添加一个静态版本字符串,而不会更改文件名,如下例所示:
-
Properties
-
YAML
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"/>)。
有关更多支持的选项,请参见 WebProperties.Resources。
欢迎页面
Spring Boot 支持静态欢迎页和模板化欢迎页。
它首先在已配置的静态内容位置中查找 index.html 文件。
如果未找到该文件,则接着查找 index 模板。
只要找到其中任意一个,便会自动将其用作应用程序的欢迎页。
此设置仅作为应用程序所定义的实际索引路由的备用方案。
排序顺序由 `HandlerMapping` 类型 Bean 的声明顺序决定,默认顺序如下:
|
使用 |
|
在 |
|
欢迎页支持 |
路径匹配与内容协商
Spring MVC 可通过检查请求路径,并将其与应用程序中定义的映射(例如,控制器方法上的 @RequestMapping 注解)进行匹配,从而将传入的 HTTP 请求映射到相应的处理器。
Spring Boot 默认禁用后缀模式匹配,这意味着类似 "GET /projects/spring-boot.json" 的请求将不会匹配到 @GetMapping("/projects/spring-boot") 的映射。
这被视为 Spring MVC 应用程序的最佳实践。
该功能在过去主要用于那些未发送正确“Accept”请求头的 HTTP 客户端;我们需要确保向客户端发送正确的内容类型(Content Type)。
如今,内容协商(Content Negotiation)已变得更加可靠。
还有其他方法可处理那些未能始终发送正确“Accept”请求头的HTTP客户端。
除了使用后缀匹配外,我们还可以使用查询参数,以确保类似 "GET /projects/spring-boot?format=json" 的请求被映射到 @GetMapping("/projects/spring-boot"):
-
Properties
-
YAML
spring.mvc.contentnegotiation.favor-parameter=true
spring:
mvc:
contentnegotiation:
favor-parameter: true
或者,如果您希望使用其他参数名称:
-
Properties
-
YAML
spring.mvc.contentnegotiation.favor-parameter=true
spring.mvc.contentnegotiation.parameter-name=myparam
spring:
mvc:
contentnegotiation:
favor-parameter: true
parameter-name: "myparam"
大多数标准媒体类型都开箱即用,但您也可以定义新的媒体类型:
-
Properties
-
YAML
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.mvc.pathmatch.matching-strategy 配置属性进行配置,如下例所示:
-
Properties
-
YAML
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 以完全禁用静态资源服务。
可配置的Web绑定初始化器
Spring MVC 使用一个 WebBindingInitializer 为特定请求初始化一个 WebDataBinder。
如果您创建了自己的 ConfigurableWebBindingInitializer @Bean,Spring Boot 将自动配置 Spring MVC 以使用它。
模板引擎
除了 REST Web 服务外,您还可以使用 Spring MVC 来提供动态 HTML 内容。 Spring MVC 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 JSP。 此外,许多其他模板引擎也提供了各自与 Spring MVC 的集成支持。
Spring Boot 包含对以下模板引擎的自动配置支持:
| 如果可能,应避免使用 JSP。 在嵌入式 Servlet 容器中使用 JSP 时存在若干已知限制。 |
当您使用这些模板引擎之一并采用默认配置时,模板会自动从 src/main/resources/templates 中加载。
| 具体取决于您运行应用程序的方式,您的集成开发环境(IDE)可能会以不同的顺序排列类路径(classpath)。 在 IDE 中通过主方法(main method)运行应用程序时,其类路径的顺序与使用 Maven 或 Gradle 运行应用程序、或从打包后的 JAR 文件运行应用程序时的顺序不同。 这可能导致 Spring Boot 无法找到预期的模板。 如果您遇到此问题,可以在 IDE 中重新调整类路径顺序,将模块的类文件和资源置于最前面。 |
错误处理
默认情况下,Spring Boot 提供了一个 /error 映射,以合理的方式处理所有错误,并将其注册为 Servlet 容器中的“全局”错误页面。
对于机器客户端,它会生成一个包含错误详情、HTTP 状态码及异常消息的 JSON 响应。
对于浏览器客户端,则提供一个“白标”(whitelabel)错误视图,以 HTML 格式渲染相同的数据(如需自定义该视图,请添加一个 View,使其解析为 error)。
如果您想自定义默认的错误处理行为,可以设置一些spring.web.error属性。
请参阅附录中的Web 属性部分。
若要完全替换默认行为,您可以实现 ErrorController,并注册该类型的 Bean 定义;或者添加一个类型为 ErrorAttributes 的 Bean,以在沿用现有机制的同时替换其内容。
BasicErrorController 可用作自定义 ErrorController 的基类。
当您希望为一种新内容类型添加处理器时(默认仅专门处理 text/html,并对其他所有类型提供回退机制),此方式尤为有用。
为此,请继承 BasicErrorController,添加一个带有 @RequestMapping 的公共方法,且该注解需具有 produces 属性,然后为您新建的类型创建一个 Bean。 |
自 Spring Framework 6.0 起,已支持 RFC 9457 问题详情(Problem Details)。
Spring MVC 可以生成采用 application/problem+json 媒体类型的自定义错误消息,例如:
{
"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 来启用此支持。
您还可以定义一个使用 @ControllerAdvice 注解的类,以自定义特定控制器和/或异常类型的 JSON 响应文档,如下例所示:
-
Java
-
Kotlin
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 的表示形式。
在某些情况下,控制器级别处理的错误不会被 Web 观测功能或指标基础设施记录。 应用程序可通过在观测上下文中设置已处理的异常,确保此类异常被观测功能记录。
自定义错误页面
如果希望为指定的状态码显示自定义 HTML 错误页面,可以将文件添加到 /error 目录中。
错误页面既可以是静态 HTML(即,置于任意静态资源目录下),也可以使用模板生成。
文件名应为精确的状态码,或采用状态码通配符格式。
例如,要将 404 映射到一个静态 HTML 文件,您的目录结构应如下所示:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
+- <other public assets>
要使用 FreeMarker 模板映射所有 5xx 错误,您的目录结构如下所示:
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.ftlh
+- <other templates>
对于更复杂的映射,您还可以添加实现 ErrorViewResolver 接口的 Bean,如下例所示:
-
Java
-
Kotlin
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 MVC 之外映射错误页面
对于不使用 Spring MVC 的应用程序,您可以使用 ErrorPageRegistrar 接口直接注册 ErrorPage 实例。
该抽象层直接与底层嵌入式 Servlet 容器交互,即使您没有 Spring MVC 的 DispatcherServlet,它也能正常工作。
-
Java
-
Kotlin
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"))
}
}
如果注册了一个 ErrorPage,其路径最终由某个 Filter 处理(这在某些非 Spring 的 Web 框架(例如 Jersey 和 Wicket)中较为常见),则该 Filter 必须显式注册为一个 ERROR 分发器,如下例所示: |
-
Java
-
Kotlin
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 分发器类型。
WAR 部署中的错误处理
当部署到 Servlet 容器时,Spring Boot 使用其错误页面过滤器(error page filter),将带有错误状态的请求转发至相应的错误页面。 这是因为 Servlet 规范并未提供用于注册错误页面的 API。 根据您所部署 WAR 文件的目标容器以及应用程序所使用的技术,可能需要进行一些额外的配置。
错误页面过滤器仅在响应尚未提交时,才能将请求转发到正确的错误页面。
默认情况下,WebSphere Application Server 8.0 及更高版本会在 Servlet 的 service 方法成功执行完毕后提交响应。
您应通过将 com.ibm.ws.webcontainer.invokeFlushAfterService 设置为 false 来禁用此行为。
CORS 支持
跨域资源共享(CORS)是一项由W3C 制定的规范,大多数浏览器均已实现该规范。它允许您以灵活的方式指定哪些类型的跨域请求被授权,从而替代了 IFRAME 或 JSONP 等安全性较低、功能较弱的方法。
自 4.2 版本起,Spring MVC 支持 CORS。
在 Spring Boot 应用程序中,使用 控制器方法级 CORS 配置 并配合 @CrossOrigin 注解,无需任何特定配置。
可通过注册一个自定义的 WebMvcConfigurer Bean,并实现自定义的 addCorsMappings(CorsRegistry) 方法,来定义 全局 CORS 配置,如下例所示:
-
Java
-
Kotlin
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 框架的参考文档。
一种映射已经添加,你还需要配置Spring MVC,以便它能够使用随请求发送的任何版本信息。 通常,版本信息作为HTTP头、查询参数或路径的一部分发送。
要配置Spring MVC,您可以使用一个<a t="C2"><code>0</code></a> bean并覆盖<code>1</code>方法,或者可以使用属性。
例如,以下内容将使用 X-Version HTTP 头来获取版本信息,并在未发送头时默认使用 1.0.0。
-
Properties
-
YAML
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
为了获得更全面的控制,您还可以定义 ApiVersionResolver、ApiVersionParser 和 ApiVersionDeprecationHandler beans,这些 beans 将被注入到自动配置的 Spring MVC 配置中。
API 版本控制也支持 WebClient 和 RestClient。
有关详细信息,请参阅 API 版本控制。 |
JAX-RS 和 Jersey
如果您更倾向于使用 JAX-RS 编程模型来构建 REST 端点,可以选用现有的某个实现,而不必使用 Spring MVC。
Jersey 和 Apache CXF 均可开箱即用,效果良好。
CXF 要求您在应用上下文中将它的 Servlet 或 Filter 注册为一个 @Bean。
Jersey 提供了一些原生的 Spring 支持,因此我们也在 Spring Boot 中为其提供了自动配置支持,并配套提供了Starters(starter)。
要开始使用 Jersey,请将 spring-boot-starter-jersey 作为依赖项引入,然后您需要一个类型为 ResourceConfig 的 @Bean 实例,并在其中注册所有端点,如下例所示:
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。
所有已注册的端点都应为带有 HTTP 资源注解(如 @GET 及其他)的 @Component,如下例所示:
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 来更改该映射。
默认情况下,Jersey 会配置为一个类型为 @Bean 的 ServletRegistrationBean 中的 Servlet,其名称为 jerseyServletRegistration。
默认情况下,该 Servlet 采用延迟初始化方式,但您可以通过设置 spring.jersey.servlet.load-on-startup 来自定义此行为。
您可以通过创建一个同名的自定义 Bean 来禁用或覆盖该 Bean。
您也可以通过设置 spring.jersey.type=filter 来使用 Filter 替代 Servlet(此时,用于替换或覆盖的 @Bean 为 jerseyFilterRegistration)。
该 Filter 具有一个 @Order,您可通过 spring.jersey.filter.order 进行设置。
当以 Filter 方式使用 Jersey 时,必须存在一个 Servlet 来处理所有未被 Jersey 拦截的请求。
如果您的应用程序中不包含此类 Servlet,则可通过将 server.servlet.register-default-servlet 设置为 true 来启用默认 Servlet。
Servlet 和 Filter 的注册均可通过使用 spring.jersey.init.* 指定属性映射来提供初始化参数。
嵌入式Servlet容器支持
对于Servlet应用程序,Spring Boot包括对嵌入式 Tomcat 和 Jetty 服务器的支持。
大多数开发人员使用适当的starter来获取一个完全配置的实例。
默认情况下,嵌入式服务器在端口 8080 上监听HTTP请求。
Servlet、过滤器和监听器
使用嵌入式 Servlet 容器时,您可以通过 Spring Bean 或扫描 Servlet 组件的方式,注册 Servlet、过滤器以及 Servlet 规范中的所有监听器(例如 HttpSessionListener)。
将Servlet、过滤器和监听器注册为Spring Bean
任何实现了 Servlet、Filter 接口或继承自 servlet *Listener 的 Spring Bean 实例,都将自动注册到嵌入式容器中。
如果希望在配置过程中引用 application.properties 中的某个值,这种方式将尤为便捷。
默认情况下,如果上下文中仅包含一个 Servlet,则它将映射到 /。
当存在多个 Servlet Bean 时,Bean 名称将用作路径前缀。
过滤器(Filter)映射到 /*。
如果基于约定的映射方式灵活性不足,您可以使用 ServletRegistrationBean、FilterRegistrationBean 和 ServletListenerRegistrationBean 类来实现完全控制。
如果您更倾向于使用注解而非 ServletRegistrationBean 和 FilterRegistrationBean,您也可以选择使用 @ServletRegistration 和
@FilterRegistration 作为替代方案。
通常可以安全地让过滤器 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_ORDER。
要查看应用程序中每个 Filter 的执行顺序,请为 web 日志组(logging.level.web=debug)启用调试级别日志记录。
注册的过滤器详细信息(包括其执行顺序和 URL 模式)将在应用启动时记录到日志中。 |
注册 Filter 类型的 Bean 时需谨慎,因为它们会在应用程序生命周期的非常早期即被初始化。
如果您需要注册一个需与其他 Bean 进行交互的 Filter,请考虑改用 DelegatingFilterProxyRegistrationBean。 |
Servlet 上下文初始化
嵌入式 Servlet 容器不会直接执行 ServletContainerInitializer 接口或 Spring 的 WebApplicationInitializer 接口。
这是有意为之的设计决策,旨在降低专为 WAR 环境设计的第三方库破坏 Spring Boot 应用程序的风险。
如果需要在 Spring Boot 应用程序中执行 Servlet 上下文初始化,应注册一个实现 ServletContextInitializer 接口的 Bean。
该接口唯一的 onStartup 方法可访问 ServletContext,并且在必要时可轻松用作现有 ServletContainerInitializer 的适配器。
初始化参数
初始化参数可在 ServletContext 上通过 server.servlet.context-parameters.* 属性进行配置。
例如,属性 server.servlet.context-parameters.com.example.parameter=example 将配置一个名为 com.example.parameter、值为 example 的 ServletContext 初始化参数。
扫描 Servlet、过滤器和监听器
使用嵌入式容器时,可通过使用 @ServletComponentScan 启用对标注了 @WebServlet、@WebFilter 和 @WebListener 的类的自动注册。
@ServletComponentScan 在独立容器中无效,此时将使用容器内置的发现机制。 |
ServletWebServerApplicationContext
Spring Boot内部使用一种不同的ApplicationContext类型来支持嵌入式servlet容器。
ServletWebServerApplicationContext是一种特殊的WebApplicationContext类型,它通过搜索单个ServletWebServerFactory bean来自行引导。
通常已经自动配置了一个TomcatServletWebServerFactory或JettyServletWebServerFactory。
您通常无需关注这些实现类。
大多数应用程序会自动配置,相应的 ApplicationContext 和 ServletWebServerFactory 将由框架为您创建。 |
在嵌入式容器配置中,ServletContext 会在应用上下文初始化期间的服务器启动过程中被设置。
正因如此,ApplicationContext 中的 Bean 无法可靠地通过 ServletContext 进行初始化。
一种绕过该问题的方法是:将 ApplicationContext 作为 Bean 的依赖项注入,并仅在实际需要时访问 ServletContext。
另一种方法是在服务器启动完成后使用回调机制。
这可以通过一个 ApplicationListener 实现,该组件监听 ApplicationStartedEvent,具体方式如下:
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.properties 或 application.yaml 文件中定义这些属性。
常见的服务器设置包括:
Spring Boot 尽最大努力公开常见设置,但并非所有情况都能做到。
对于这些情况,专用命名空间提供了服务器特定的自定义选项(参见 server.tomcat)。
例如,访问日志 可以使用嵌入式 servlet 容器的特定功能进行配置。
请参阅 ServerProperties 类以获取完整列表。 |
SameSite Cookie
SameSite Cookie 属性可用于 Web 浏览器,以控制 Cookie 在跨站请求中是否提交以及如何提交。
该属性对现代 Web 浏览器尤为重要,因为这些浏览器已开始更改在缺失该属性时所采用的默认值。
如果您想更改会话 cookie 的 SameSite 属性,可以使用 server.servlet.session.cookie.same-site 属性。
此属性由自动配置的 Tomcat 和 Jetty 服务器支持。
它还用于配置基于 Spring Session 的 servlet SessionRepository beans。
例如,如果您希望会话 Cookie 的 SameSite 属性值为 None,则可以在您的 application.properties 或 application.yaml 文件中添加以下配置:
-
Properties
-
YAML
server.servlet.session.cookie.same-site=none
server:
servlet:
session:
cookie:
same-site: "none"
如果您希望修改添加到您的 HttpServletResponse 中的其他 Cookie 的 SameSite 属性,可以使用 CookieSameSiteSupplier。
CookieSameSiteSupplier 会接收一个 Cookie,并可返回一个 SameSite 值,或返回 null。
提供了许多便捷的工厂方法和过滤器方法,可帮助您快速匹配特定的 Cookie。
例如,添加以下 Bean 将自动为所有名称与正则表达式 myapp.* 匹配的 Cookie 设置 SameSite 为 Lax。
-
Java
-
Kotlin
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.*")
}
}
字符编码
可使用 server.servlet.encoding.* 配置属性来配置嵌入式 Servlet 容器在请求和响应处理中的字符编码行为。
当请求的 Accept-Language 请求头指明了该请求所用的区域设置(locale)时,Servlet 容器将自动将其映射为相应的字符集(charset)。
每个容器都提供了默认的区域设置到字符集的映射关系,您应验证这些默认映射是否满足您应用程序的需求。
若不满足,请使用 server.servlet.encoding.mapping 配置属性来自定义映射关系,如下例所示:
-
Properties
-
YAML
server.servlet.encoding.mapping.ko=UTF-8
server:
servlet:
encoding:
mapping:
ko: "UTF-8"
在前面的示例中,ko(韩语)区域设置已映射到 UTF-8。
这相当于传统 WAR 部署中 web.xml 文件中的一个 <locale-encoding-mapping-list> 条目。
编程式自定义
如果需要以编程方式配置嵌入式 Servlet 容器,可以注册一个实现 WebServerFactoryCustomizer 接口的 Spring Bean。
WebServerFactoryCustomizer 提供对 ConfigurableServletWebServerFactory 的访问,后者包含大量用于自定义的 setter 方法。
以下示例展示了如何以编程方式设置端口号:
-
Java
-
Kotlin
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)
}
}
TomcatServletWebServerFactory 和 JettyServletWebServerFactory 是 ConfigurableServletWebServerFactory 的专用变体,分别提供了针对 Tomcat 和 Jetty 的额外自定义设置方法。
下面的示例展示了如何自定义 TomcatServletWebServerFactory,该方法提供了对 Tomcat 特定配置选项的访问:
-
Java
-
Kotlin
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。
为许多配置选项提供了 Setter 方法。
此外,还提供了若干受保护的方法“钩子”,以便您在需要执行更复杂操作时使用。
详情请参阅 ConfigurableServletWebServerFactory 的 API 文档。
| 自动配置的自定义器仍会应用于您的自定义工厂,因此请谨慎使用该选项。 |
JSP 局限性
当运行使用嵌入式 Servlet 容器(并打包为可执行归档文件)的 Spring Boot 应用程序时,JSP 支持存在一些限制。
-
使用 Jetty 和 Tomcat 时,若采用 WAR 包格式,则应可正常运行。 可执行的 WAR 包在通过
java -jar启动时可以正常运行,同时也可部署到任何标准容器中。 使用可执行 JAR 包时,不支持 JSP。 -
如果您使用
mvn spring-boot:run或gradle bootRun运行您的应用程序,并且偏离了标准src/main/webapp目录结构,您可能需要设置一个WAR_SOURCE_DIRECTORY环境变量,以便 Spring Boot 能找到您的 JSP 文件。