Servlet Web 应用程序
如果您想构建基于 servlet 的 Web 应用程序,您可以利用 Spring Boot 对 Spring MVC 或 Jersey 的自动配置。
“Spring Web MVC 框架”
Spring Web MVC 框架(通常称为“Spring MVC”)是一个丰富的“模型视图控制器”Web 框架。
Spring MVC 允许您创建特殊的 @Controller
或 @RestController
bean 来处理传入的 HTTP 请求。
控制器中的方法通过使用 @RequestMapping
注解映射到 HTTP。
以下代码显示了提供 JSON 数据的典型 @RestController
:
-
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 Framework 的一部分,详细信息可在参考文档中找到。 spring.io/guides 上还提供了几个涵盖 Spring MVC 的指南。
你可以定义任意数量的 RouterFunction bean 来模块化路由器的定义。
如果需要应用优先级,则可以对 bean 进行排序。 |
Spring MVC 自动配置
Spring Boot 为 Spring MVC 提供了自动配置,适用于大多数应用程序。
它取代了对 @EnableWebMvc
的需求,两者不能一起使用。
除了 Spring MVC 的默认值之外,自动配置还提供以下功能:
-
包括
ContentNegotiatingViewResolver
和BeanNameViewResolver
bean。 -
支持提供静态资源,包括对 WebJar 的支持(本文档稍后将介绍)。
-
自动注册
Converter
、GenericConverter
和Formatter
bean。 -
支持
HttpMessageConverters
(本文档稍后将介绍)。 -
自动注册
MessageCodesResolver
(本文档稍后将介绍)。 -
静态支撑。
index.html
-
自动使用
ConfigurableWebBindingInitializer
Bean(本文档稍后将介绍)。
如果你想保留那些 Spring Boot MVC 定制并进行更多的 MVC 定制(拦截器、格式化程序、视图控制器和其他功能),你可以添加你自己的 @Configuration 类型的 WebMvcConfigurer
类,但没有@EnableWebMvc
。
如果要提供RequestMappingHandlerMapping
、RequestMappingHandlerAdapter
或ExceptionHandlerExceptionResolver
的自定义实例,并且仍然保留 Spring Boot MVC 自定义,则可以声明WebMvcRegistrations
类型的 Bean,并使用它来提供这些组件的自定义实例。
自定义实例将受 Spring MVC 的进一步初始化和配置的约束。
要参与并在需要时覆盖该后续处理,应使用WebMvcConfigurer
。
如果您不想使用自动配置并希望完全控制 Spring MVC,请添加您自己的@Configuration
并带有 @EnableWebMvc
注释。
或者,按照 @EnableWebMvc
API 文档中所述,添加您自己的@Configuration
注释的DelegatingWebMvcConfiguration
。
Spring MVC 转换服务
Spring MVC 使用与用于转换 or 文件中的值不同的 ConversionService
。
这意味着 Period
、Duration
和 DataSize
转换器不可用,并且 @DurationUnit
和 @DataSizeUnit
注释将被忽略。application.properties
application.yaml
如果要自定义 Spring MVC 使用的ConversionService
,则可以为WebMvcConfigurer
bean 提供一个方法。
通过此方法,您可以注册所需的任何转换器,也可以委托给 ApplicationConversionService
上可用的静态方法。addFormatters
还可以使用配置属性自定义转换。
如果未配置,则使用以下默认值:spring.mvc.format.*
财产 | DateTimeFormatter |
格式 |
---|---|---|
|
|
|
|
|
java.time 的 |
|
|
java.time 的 |
HttpMessage转换器
Spring MVC 使用HttpMessageConverter
接口来转换 HTTP 请求和响应。
合理的默认值是开箱即用的。
例如,对象可以自动转换为 JSON(通过使用 Jackson 库)或 XML(通过使用 Jackson XML 扩展,如果可用,或者通过使用 JAXB,如果 Jackson XML 扩展不可用)。
默认情况下,字符串以 .UTF-8
上下文中存在的任何HttpMessageConverter
bean 都将被添加到转换器列表中。
您也可以以相同的方式覆盖默认转换器。
如果需要添加或自定义转换器,可以使用 Spring Boot 的HttpMessageConverters
类,如下面的清单所示:
-
Java
-
Kotlin
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
@Configuration(proxyBeanMethods = false)
public class MyHttpMessageConvertersConfiguration {
@Bean
public HttpMessageConverters customConverters() {
HttpMessageConverter<?> additional = new AdditionalHttpMessageConverter();
HttpMessageConverter<?> another = new AnotherHttpMessageConverter();
return new HttpMessageConverters(additional, another);
}
}
import org.springframework.boot.autoconfigure.http.HttpMessageConverters
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.converter.HttpMessageConverter
@Configuration(proxyBeanMethods = false)
class MyHttpMessageConvertersConfiguration {
@Bean
fun customConverters(): HttpMessageConverters {
val additional: HttpMessageConverter<*> = AdditionalHttpMessageConverter()
val another: HttpMessageConverter<*> = AnotherHttpMessageConverter()
return HttpMessageConverters(additional, another)
}
}
为了进一步控制,您还可以子类 HttpMessageConverters
并覆盖其 and/or 方法。
当您想要重新排序或删除 Spring MVC 默认配置的某些转换器时,这可能很有用。postProcessConverters
postProcessPartConverters
MessageCodes解析器
Spring MVC 有一个生成错误代码的策略,用于呈现来自绑定错误的错误消息:MessageCodesResolver
。
如果设置属性或,则 Spring Boot 会为您创建一个(请参见DefaultMessageCodesResolver.Format
中的枚举)。spring.mvc.message-codes-resolver-format
PREFIX_ERROR_CODE
POSTFIX_ERROR_CODE
静态内容
默认情况下, Spring Boot 从 Classpath 中名为 (or or or or ) 的目录或 ServletContext
的根目录提供静态内容。
它使用 Spring MVC 中的ResourceHttpRequestHandler
,以便你可以通过添加自己的WebMvcConfigurer
并覆盖该方法来修改该行为。/static
/public
/resources
/META-INF/resources
addResourceHandlers
在独立的 Web 应用程序中,未启用容器中的默认 Servlet。
可以使用 属性 来启用它。server.servlet.register-default-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/**"
您还可以使用属性自定义静态资源位置(将默认值替换为目录位置列表)。
根 Servlet 上下文路径 也会自动添加为位置。spring.web.resources.static-locations
"/"
除了前面提到的“标准”静态资源位置之外,Webjars 内容还有一个特殊情况。
默认情况下,如果 jar 文件以 Webjars 格式打包,则从 jar 文件提供任何具有路径 in 的资源。
可以使用 属性 自定义路径。/webjars/**
spring.mvc.webjars-path-pattern
如果您的应用程序打包为 jar,请不要使用该目录。
虽然这个目录是一个通用标准,但它仅适用于 war 打包,如果你生成一个 jar,大多数构建工具都会默默地忽略它。src/main/webapp |
Spring Boot 还支持 Spring MVC 提供的高级资源处理功能,允许使用诸如缓存清除静态资源或对 Webjar 使用与版本无关的 URL 等用例。
要对 Webjar 使用与版本无关的 URL,请添加依赖项。
然后声明您的 Webjar。
以 jQuery 为例,添加 where 是 Webjar 版本。org.webjars:webjars-locator-lite
"/webjars/jquery/jquery.min.js"
"/webjars/jquery/x.y.z/jquery.min.js"
x.y.z
要使用缓存清除,以下配置为所有静态资源配置缓存清除解决方案,从而有效地在 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” 策略在 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"
通过此配置,位于 下的 JavaScript 模块使用固定的版本控制策略 (),而其他资源仍然使用内容 1 ()。"/js/lib/"
"/v12/js/lib/mymodule.js"
<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>
有关更多支持的选项,请参阅 WebProperties.Resources
。
欢迎页面
Spring Boot 支持静态和模板化欢迎页面。
它首先在配置的静态内容位置中查找文件。
如果未找到,则查找模板。
如果找到任何一个,它将自动用作应用程序的欢迎页面。index.html
index
这仅充当应用程序定义的实际索引路由的回退。
排序由 HandlerMapping
bean 的顺序定义,默认情况下如下:
|
使用 |
|
在 |
|
欢迎页面支持 |
路径匹配和内容协商
Spring MVC 可以通过查看请求路径并将其与应用程序中定义的映射(例如,Controller 方法上的 @GetMapping
注释)进行匹配,从而将传入的 HTTP 请求映射到处理程序。
Spring Boot 默认选择禁用后缀模式匹配,这意味着像这样的请求将不会与 Map 匹配。
这被认为是 Spring MVC 应用程序的最佳实践。
此功能在过去主要对没有发送正确的 “Accept” 请求标头的 HTTP 客户端有用;我们需要确保将正确的 Content Type 发送给客户端。
如今,Content Negotiation 更加可靠。"GET /projects/spring-boot.json"
@GetMapping("/projects/spring-boot")
还有其他方法可以处理 HTTP 客户端,这些客户端不能始终如一地发送正确的 “Accept” 请求标头。
我们可以使用查询参数来确保像这样的请求将被映射到 :"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
限制某些 path pattern 变体的使用。
它也与使用路径前缀 () 配置 DispatcherServlet
不兼容。spring.mvc.servlet.path
可以使用 configuration 属性配置策略,如以下示例所示: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
,请设置为更具体的值,例如 or 设置为 以完全禁用静态内容的提供。/**
spring.mvc.static-path-pattern
/resources/**
spring.web.resources.add-mappings
false
ConfigurableWebBindingInitializer
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 容器一起使用时,存在几个已知的限制。 |
当您将其中一个模板引擎与默认配置一起使用时,您的模板会自动从 中选取。src/main/resources/templates
根据您运行应用程序的方式,IDE 可能会以不同的方式对 Classpath 进行排序。 在 IDE 中,从 main 方法运行应用程序时,与使用 Maven 或 Gradle 或从其打包的 jar 运行应用程序时的顺序不同。 这可能会导致 Spring Boot 无法找到预期的模板。 如果遇到此问题,可以在 IDE 中对 Classpath 重新排序,以便将模块的类和资源放在最前面。 |
错误处理
默认情况下, Spring Boot 提供了一个以合理方式处理所有错误的 Map,并且在 servlet 容器中注册为“全局”错误页面。
对于计算机客户端,它会生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。
对于浏览器客户端,有一个“whitelabel”错误视图,它以 HTML 格式呈现相同的数据(要对其进行自定义,请添加一个 View
thats resolves to )。/error
error
如果要自定义默认错误处理行为,可以设置许多属性。
请参阅附录的 Server Properties 部分。server.error
要完全替换默认行为,你可以实现ErrorController
并注册该类型的 Bean 定义,或者添加ErrorAttributes
类型的 Bean 以使用现有机制但替换内容。
BasicErrorController 可以用作自定义 ErrorController 的基类。
如果要为新的内容类型添加处理程序(默认是专门处理并为其他所有内容提供回退),这将特别有用。
为此,请扩展 BasicErrorController ,添加一个具有属性的 @RequestMapping 的公共方法,并创建一个新类型的 Bean。text/html produces |
从 Spring Framework 6.0 开始,支持 RFC 9457 问题详细信息。
Spring MVC 可以生成具有 media 类型的自定义错误消息,例如: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
}
}
在前面的示例中,如果由在与 相同的包中定义的控制器抛出,则使用 POJO 的 JSON 表示形式,而不是 ErrorAttributes
表示形式。MyException
SomeController
MyErrorBody
在某些情况下,Web 观测或指标基础设施不会记录在控制器级别处理的错误。 应用程序可以通过在观察上下文中设置已处理的异常来确保此类异常与观察一起记录。
自定义错误页面
如果要显示给定状态代码的自定义 HTML 错误页面,可以将文件添加到目录中。
错误页面可以是静态 HTML(即,添加到任何静态资源目录下),也可以使用模板构建。
文件名应为确切的状态代码或系列掩码。/error
例如,要映射到静态 HTML 文件,您的目录结构将如下所示:404
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.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 MVC 之外映射错误页面
对于不使用 Spring MVC 的应用程序,你可以使用ErrorPageRegistrar
接口直接注册ErrorPage
实例。
这种抽象直接与底层嵌入式 servlet 容器一起工作,即使你没有 Spring MVC DispatcherServlet
也能工作。
-
Java
-
Kotlin
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"))
}
}
如果使用最终由 Filter 处理的路径注册 ErrorPage (这在一些非 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
不包括 dispatcher 类型。ERROR
WAR 部署中的错误处理
当部署到 servlet 容器时, Spring Boot 使用其错误页面过滤器将具有错误状态的请求转发到相应的错误页面。 这是必需的,因为 servlet 规范没有提供用于注册错误页面的 API。 根据要将 war 文件部署到的容器和应用程序使用的技术,可能需要一些额外的配置。
如果尚未提交响应,则错误页面过滤器只能将请求转发到正确的错误页面。
默认情况下,WebSphere Application Server 8.0 及更高版本在成功完成 servlet 的服务方法后提交响应。
您应该通过设置为 来禁用此行为。com.ibm.ws.webcontainer.invokeFlushAfterService
false
CORS 支持
从版本 4.2 开始,Spring MVC 支持 CORS。
在 Spring Boot 应用程序中使用带有 @CrossOrigin
注释的控制器方法 CORS 配置不需要任何特定配置。可以通过使用自定义方法注册WebMvcConfigurer
bean 来定义全局 CORS 配置,如以下示例所示:addCorsMappings(CorsRegistry)
-
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/**")
}
}
}
}
JAX-RS 和Jersey
如果您更喜欢 REST 端点的 JAX-RS 编程模型,则可以使用其中一个可用的实现,而不是 Spring MVC。Jersey 和 Apache CXF 开箱即用。
CXF 要求您在应用程序上下文中将其 Servlet
或 Filter
注册为 @Bean
。
Jersey 有一些原生的 Spring 支持,因此我们还在 Spring Boot 中提供了对它的自动配置支持,以及一个Starters。
要开始使用 Jersey,请将 作为依赖项包含在内,然后您需要一个 ResourceConfig
类型的@Bean
,您可以在其中注册所有终端节点,如以下示例所示:spring-boot-starter-jersey
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;
@Component
public class MyJerseyConfig extends ResourceConfig {
public MyJerseyConfig() {
register(MyEndpoint.class);
}
}
Jersey 对扫描可执行档案的支持相当有限。
例如,它无法扫描在完全可执行的 jar 文件或运行可执行 war 文件时找到的包中的端点。
为避免此限制,不应使用该方法,并且应使用该方法单独注册端点,如前面的示例所示。WEB-INF/classes packages register |
对于更高级的自定义,您还可以注册任意数量的实现ResourceConfigCustomizer
的 bean。
所有已注册的终端节点都应该是带有 HTTP 资源注释(和其他)的 @Component
,如以下示例所示:@GET
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 被设置为名为 的 ServletRegistrationBean
类型的@Bean
中的 Servlet。
默认情况下,Servlet 是延迟初始化的,但您可以通过设置 来自定义该行为。
您可以通过创建一个具有相同名称的 bean 来禁用或覆盖该 bean。
您还可以通过设置 (在这种情况下,要替换或覆盖的@Bean
) 来使用过滤器而不是 servlet 。
过滤器具有@Order
,您可以使用 进行设置。
当使用 Jersey 作为过滤器时,必须存在一个 Servlet,该 Servlet 将处理 Jersey 未拦截的任何请求。
如果应用程序不包含此类 Servlet,则可能需要通过设置为 来启用默认 Servlet。
servlet 和过滤器注册都可以通过 using 指定属性映射来获得 init 参数。jerseyServletRegistration
spring.jersey.servlet.load-on-startup
spring.jersey.type=filter
jerseyFilterRegistration
spring.jersey.filter.order
server.servlet.register-default-servlet
true
spring.jersey.init.*
嵌入式 Servlet 容器支持
对于 servlet 应用程序,Spring Boot 包括对嵌入式 Tomcat、Jetty 和 Undertow 服务器的支持。
大多数开发人员使用适当的 starter 来获取完全配置的实例。
默认情况下,嵌入式服务器侦听端口 上的 HTTP 请求。8080
Servlet、过滤器和侦听器
使用嵌入式 Servlet 容器时,可以通过使用 Spring Bean 或扫描 Servlet 组件,从 Servlet 规范注册 Servlet、过滤器和所有侦听器(例如 HttpSessionListener
)。
将 Servlet、过滤器和侦听器注册为 Spring Bean
任何作为 Spring Bean 的 Servlet
、Filter
或 Servlet 实例都注册到嵌入式容器中。
如果您想在配置期间引用 your 中的值,这可能特别方便。*Listener
application.properties
默认情况下,如果上下文仅包含单个 Servlet,则会将其映射到 。
在多个 servlet bean 的情况下,bean 名称用作路径前缀。
筛选器映射到 。/
/*
如果基于约定的映射不够灵活,则可以使用 ServletRegistrationBean
、FilterRegistrationBean
和 ServletListenerRegistrationBean
类进行完全控制。
通常,将 filter bean 保持无序状态是安全的。
如果需要特定的顺序,则应使用 @Order
注释 Filter
或使其实现 Ordered
。
不能通过使用 @Order
Comments Filter 的 bean 方法来配置 Filter
的顺序。
如果无法更改 Filter
类以添加 @Order
或实现 Ordered
,则必须为 Filter
定义 FilterRegistrationBean
,并使用该方法设置注册 Bean 的顺序。
避免配置读取请求正文的过滤器,因为它可能会违背应用程序的字符编码配置。
如果 servlet 过滤器包装请求,则应将其配置为小于或等于 的顺序。setOrder(int)
Ordered.HIGHEST_PRECEDENCE
OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER
要查看应用程序中每个 Filter 的顺序,请为 logging 组 () 启用调试级别日志记录。
然后,将在启动时记录已注册过滤器的详细信息,包括它们的顺序和 URL 模式。web logging.level.web=debug |
注册 Filter bean 时要小心,因为它们在应用程序生命周期的早期就被初始化了。
如果需要注册与其他 bean 交互的Filter ,请考虑改用DelegatingFilterProxyRegistrationBean 。 |
Servlet 上下文初始化
嵌入式 Servlet 容器不直接执行 ServletContainerInitializer
接口或 Spring 的WebApplicationInitializer
接口。
这是一个有意的设计决策,旨在降低设计为在 war 中运行的第三方库可能会破坏 Spring Boot 应用程序的风险。
如果需要在 Spring Boot 应用程序中执行 servlet 上下文初始化,则应注册一个实现ServletContextInitializer
接口的 bean。
单个方法提供对ServletContext
的访问,并且如有必要,可以很容易地用作现有WebApplicationInitializer
的适配器。onStartup
扫描 Servlet、Filter 和侦听器
使用嵌入式容器时,可以使用 @ServletComponentScan
启用@WebServlet
、@WebFilter
和 @WebListener
批注的类的自动注册。
@ServletComponentScan 在独立容器中不起作用,其中使用容器的内置发现机制。 |
The ServletWebServerApplicationContext
在后台, Spring Boot 使用不同类型的ApplicationContext
来支持嵌入式 servlet 容器。
ServletWebServerApplicationContext
是一种特殊类型的WebApplicationContext
,它通过搜索单个ServletWebServerFactory
bean 来引导自身。
通常,TomcatServletWebServerFactory
、JettyServletWebServerFactory
或UndertowServletWebServerFactory
已自动配置。
您通常不需要了解这些 implementation classes。
大多数应用程序都是自动配置的,并且相应的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 容器
可以使用 Spring Environment
属性配置常见的 servlet 容器设置。
通常,您将在 or 文件中定义属性。application.properties
application.yaml
常见的服务器设置包括:
Spring Boot 会尽可能多地公开通用设置,但这并不总是可能的。
对于这些情况,专用命名空间提供特定于服务器的自定义(请参阅 和 )。
例如,可以使用嵌入式 servlet 容器的特定功能配置访问日志。server.tomcat
server.undertow
有关完整列表,请参阅 ServerProperties 类。 |
SameSite Cookie
Web 浏览器可以使用 cookie 属性来控制是否以及如何在跨站点请求中提交 cookie。
该属性与现代 Web 浏览器尤其相关,这些浏览器已开始更改在缺少该属性时使用的默认值。SameSite
如果要更改会话 Cookie 的属性,可以使用该属性。
自动配置的 Tomcat、Jetty 和 Undertow 服务器支持此属性。
它还用于配置基于 Spring Session servlet 的 SessionRepository
bean。SameSite
server.servlet.session.cookie.same-site
例如,如果您希望会话 Cookie 具有 属性 ,则可以将以下内容添加到您的 or 文件中:SameSite
None
application.properties
application.yaml
-
Properties
-
YAML
server.servlet.session.cookie.same-site=none
server:
servlet:
session:
cookie:
same-site: "none"
如果要更改添加到 HttpServletResponse
的其他 Cookie 的属性,可以使用 CookieSameSiteSupplier
。
CookieSameSiteSupplier
传递一个 Cookie
,并可能返回一个值或 .SameSite
SameSite
null
您可以使用许多方便的 Factory 和 Filter 方法来快速匹配特定的 Cookie。
例如,添加以下 bean 将自动对名称与 regular expression 匹配的所有 cookie 应用 of 。SameSite
Lax
myapp.*
-
Java
-
Kotlin
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.*
当请求的标头指示请求的语言环境时,Servlet 容器会自动将其映射到字符集。
每个容器都提供 charset 映射的默认区域设置,您应该验证它们是否满足应用程序的需要。
如果它们没有,请使用 configuration 属性来自定义映射,如以下示例所示:Accept-Language
server.servlet.encoding.mapping
-
Properties
-
YAML
server.servlet.encoding.mapping.ko=UTF-8
server:
servlet:
encoding:
mapping:
ko: "UTF-8"
在前面的示例中,(朝鲜语)区域设置已映射到 。
这相当于传统 war 部署文件中的条目。ko
UTF-8
<locale-encoding-mapping-list>
web.xml
编程自定义
如果需要以编程方式配置嵌入式 Servlet 容器,则可以注册实现WebServerFactoryCustomizer
接口的 Spring Bean。WebServerFactoryCustomizer
提供对ConfigurableServletWebServerFactory
的访问,其中包括许多自定义 setter 方法。
以下示例显示了以编程方式设置端口:
-
Java
-
Kotlin
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
、JettyServletWebServerFactory
和UndertowServletWebServerFactory
是ConfigurableServletWebServerFactory
的专用变体,它们分别具有针对Tomcat、Jetty和Undertow的其他自定义设置器方法。
下面的示例展示了如何自定义TomcatServletWebServerFactory
,该工厂提供对特定于 Tomcat 的配置选项的访问:
-
Java
-
Kotlin
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。
为许多配置选项提供了 setter。
如果你需要做一些更奇特的事情,还提供了几个受保护的方法 “钩子”。
有关详细信息,请参见ConfigurableServletWebServerFactory
API 文档。
自动配置的定制器仍应用于您的自定义工厂,因此请谨慎使用该选项。 |