此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Boot 3.3.1! |
此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Boot 3.3.1! |
如果要构建基于 servlet 的 Web 应用程序,可以利用 Spring Boot 对 Spring MVC 或 Jersey 的自动配置。
“Spring Web MVC 框架”
Spring Web MVC 框架(通常称为“Spring MVC”)是一个丰富的“模型视图控制器”Web 框架。
Spring MVC 允许您创建特殊或 Bean 来处理传入的 HTTP 请求。
控制器中的方法使用注释映射到 HTTP。@Controller
@RestController
@RequestMapping
以下代码显示了提供 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) {
/**/
return ServerResponse.ok().build();
}
public ServerResponse getUserCustomers(ServerRequest request) {
/**/
return ServerResponse.ok().build();
}
public ServerResponse deleteUser(ServerRequest request) {
/**/
return ServerResponse.ok().build();
}
}
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 {
return ServerResponse.ok().build()
}
fun getUserCustomers(request: ServerRequest?): ServerResponse {
return ServerResponse.ok().build()
}
fun deleteUser(request: ServerRequest?): ServerResponse {
return ServerResponse.ok().build()
}
}
Spring MVC 是核心 Spring Framework 的一部分,详细信息可在参考文档中找到。 还有一些涵盖 Spring MVC 的指南可在 spring.io/guides 上找到。
您可以根据需要定义任意数量的 Bean 来模块化路由器的定义。
如果您需要应用优先级,可以订购 Bean。RouterFunction |
Spring MVC 自动配置
Spring Boot 为 Spring MVC 提供了自动配置,适用于大多数应用程序。
它取代了对两者的需要,两者不能一起使用。
除了 Spring MVC 的默认值外,自动配置还提供以下功能:@EnableWebMvc
如果要保留这些 Spring Boot MVC 自定义并进行更多 MVC 自定义(拦截器、格式化程序、视图控制器和其他功能),则可以添加自己的类型类,但不添加 .@Configuration
WebMvcConfigurer
@EnableWebMvc
如果要提供 、 或 的自定义实例,并且仍保留 Spring Boot MVC 自定义项,则可以声明 type 的 bean 并使用它来提供这些组件的自定义实例。
自定义实例将由 Spring MVC 进一步初始化和配置。
若要参与(如果需要)覆盖该后续处理,应使用 a。RequestMappingHandlerMapping
RequestMappingHandlerAdapter
ExceptionHandlerExceptionResolver
WebMvcRegistrations
WebMvcConfigurer
如果您不想使用自动配置,并且想完全控制 Spring MVC,请添加您自己的 annoted with .
或者,添加您自己的 -annotated,如 的 Javadoc 中所述。@Configuration
@EnableWebMvc
@Configuration
DelegatingWebMvcConfiguration
@EnableWebMvc
Spring MVC 转换服务
Spring MVC 使用与用于转换 your 或文件中的值不同的方法。
这意味着 和 转换器不可用,并且 和 注释将被忽略。ConversionService
application.properties
application.yaml
Period
Duration
DataSize
@DurationUnit
@DataSizeUnit
如果要自定义 Spring MVC 使用的,可以为 Bean 提供一个方法。
通过此方法,您可以注册您喜欢的任何转换器,也可以委托给 上可用的静态方法。ConversionService
WebMvcConfigurer
addFormatters
ApplicationConversionService
也可以使用配置属性自定义转换。
如果未配置,则使用以下默认值:spring.mvc.format.*
财产 | DateTimeFormatter |
---|---|
|
|
|
|
|
|
HttpMessageConverters
Spring MVC 使用该接口来转换 HTTP 请求和响应。
合理的默认值是开箱即用的。
例如,可以自动将对象转换为 JSON(使用 Jackson 库)或 XML(使用 Jackson XML 扩展(如果可用)或使用 JAXB(如果 Jackson XML 扩展不可用)。
默认情况下,字符串以 .HttpMessageConverter
UTF-8
上下文中存在的任何 Bean 都将添加到转换器列表中。
您也可以以相同的方式覆盖默认转换器。HttpMessageConverter
如果需要添加或自定义转换器,可以使用 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)
}
}
为了进一步控制,您还可以子类化并重写其 and/or 方法。
当您想要重新排序或删除 Spring MVC 默认配置的某些转换器时,这可能很有用。HttpMessageConverters
postProcessConverters
postProcessPartConverters
MessageCodesResolver
Spring MVC 有一个策略来生成错误代码,用于从绑定错误中呈现错误消息:。
如果设置了属性 或 ,Spring Boot 将为您创建一个(请参阅 DefaultMessageCodesResolver.Format
中的枚举)。MessageCodesResolver
spring.mvc.message-codes-resolver-format
PREFIX_ERROR_CODE
POSTFIX_ERROR_CODE
静态内容
默认情况下,Spring Boot 从类路径中名为 (or or or ) 的目录或 .
它使用 from Spring MVC,以便您可以通过添加自己的方法并重写该方法来修改该行为。/static
/public
/resources
/META-INF/resources
ServletContext
ResourceHttpRequestHandler
WebMvcConfigurer
addResourceHandlers
在独立 Web 应用程序中,未启用容器中的缺省 Servlet。
可以使用该属性启用它。server.servlet.register-default-servlet
默认 servlet 充当后备,从 if Spring 决定不处理它的根目录提供内容。
大多数情况下,这不会发生(除非您修改默认的 MVC 配置),因为 Spring 始终可以通过 .ServletContext
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 内容还有一个特例。
默认情况下,如果以 Webjars 格式打包,则任何带有路径的资源都会从 jar 文件中提供。
可以使用属性自定义路径。/webjars/**
spring.mvc.webjars-path-pattern
如果应用程序打包为 jar,请不要使用该目录。
虽然这个目录是一个通用标准,但它只适用于战争打包,如果你生成一个 jar,大多数构建工具都会默默地忽略它。src/main/webapp |
Spring Boot 还支持 Spring MVC 提供的高级资源处理功能,允许使用例,例如缓存破坏静态资源或对 Webjar 使用与版本无关的 URL。
要对 Webjar 使用与版本无关的 URL,请添加依赖项。
然后声明你的 Webjar。
以 jQuery 为例,在 Webjar 版本的位置添加结果。webjars-locator-core
"/webjars/jquery/jquery.min.js"
"/webjars/jquery/x.y.z/jquery.min.js"
x.y.z
如果使用 JBoss,则需要声明依赖项,而不是 .
否则,所有 Webjar 都解析为 .webjars-locator-jboss-vfs webjars-locator-core 404 |
若要使用缓存破坏,以下配置为所有静态资源配置缓存破坏解决方案,从而有效地在 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 自动配置了 Thymeleaf 的链接,因此在运行时在模板中重写了指向资源的链接。
使用 JSP 时,应手动声明此过滤器。
目前不自动支持其他模板引擎,但可以使用自定义模板宏/帮助程序和 ResourceUrlProvider 的使用。ResourceUrlEncodingFilter |
例如,使用 JavaScript 模块加载器动态加载资源时,重命名文件不是一个选项。 这就是为什么其他策略也得到支持并且可以组合的原因。 “固定”策略在不更改文件名的情况下在 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 模块使用固定的版本控制策略 (),而其他资源仍使用内容 ()。"/js/lib/"
"/v12/js/lib/mymodule.js"
<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>
有关更多支持的选项,请参阅 WebProperties.Resources
。
欢迎页面
Spring Boot 支持静态和模板化欢迎页面。
它首先在配置的静态内容位置中查找文件。
如果找不到模板,则查找模板。
如果找到任一,则会自动将其用作应用程序的欢迎页面。index.html
index
这仅充当应用程序定义的实际索引路由的回退。
顺序由 Bean 的顺序定义,默认如下:HandlerMapping
|
使用 Bean 声明的端点 |
|
在 Bean 中声明的端点 |
|
欢迎页面支持 |
路径匹配和内容协商
Spring MVC 可以通过查看请求路径并将其与应用程序中定义的映射(例如,Controller 方法上的注释)匹配,将传入的 HTTP 请求映射到处理程序。@GetMapping
Spring Boot 默认选择禁用后缀模式匹配,这意味着 like 请求不会与映射匹配。
这被认为是 Spring MVC 应用程序的最佳实践。
此功能过去主要用于未发送正确“接受”请求标头的 HTTP 客户端;我们需要确保向客户端发送正确的内容类型。
如今,内容协商更加可靠。"GET /projects/spring-boot.json"
@GetMapping("/projects/spring-boot")
还有其他方法可以处理 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
可以使用 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 将发送 404 Not Found 错误响应。
要改为抛出,请将 configprop:spring.mvc.throw-exception-if-no-handler-found 设置为 。
请注意,默认情况下,静态内容的提供映射到所有请求,因此将为所有请求提供处理程序。
对于要抛出的 a,还必须设置为更具体的值,例如或设置为完全禁用静态内容的提供。NoHandlerFoundException
true
/**
NoHandlerFoundException
spring.mvc.static-path-pattern
/resources/**
spring.web.resources.add-mappings
false
可配置的WebBindingInitializer
Spring MVC 使用 a 为特定请求初始化 a。
如果您创建自己的 ,Spring Boot 会自动配置 Spring MVC 以使用它。WebBindingInitializer
WebDataBinder
ConfigurableWebBindingInitializer
@Bean
模板引擎
除了 REST Web 服务,您还可以使用 Spring MVC 来提供动态 HTML 内容。 Spring MVC 支持多种模板技术,包括 Thymeleaf、FreeMarker 和 JSP。 此外,许多其他模板引擎都包含自己的 Spring MVC 集成。
Spring Boot 包括对以下模板引擎的自动配置支持:
如果可能,应避免使用 JSP。 将它们与嵌入式 servlet 容器一起使用时,存在一些已知的限制。 |
当您将其中一个模板引擎与默认配置一起使用时,您的模板将自动从 中选取。src/main/resources/templates
根据应用程序的运行方式,IDE 可能会以不同的方式对类路径进行排序。 在 IDE 中从 main 方法运行应用程序的顺序与使用 Maven 或 Gradle 或从其打包的 jar 运行应用程序时的顺序不同。 这可能会导致 Spring Boot 无法找到预期的模板。 如果遇到此问题,可以在 IDE 中对类路径重新排序,以将模块的类和资源放在首位。 |
错误处理
默认情况下,Spring Boot 提供了一个以合理方式处理所有错误的映射,并在 servlet 容器中注册为“全局”错误页面。
对于计算机客户端,它会生成一个 JSON 响应,其中包含错误、HTTP 状态和异常消息的详细信息。
对于浏览器客户端,有一个“白标”错误视图,它以 HTML 格式呈现相同的数据(要自定义它,请添加解析为 )。/error
View
error
如果要自定义默认错误处理行为,可以设置许多属性。
请参阅附录的“服务器属性”部分。server.error
要完全替换缺省行为,可以实现并注册该类型的 Bean 定义,或者添加类型 Bean 以使用现有机制但替换内容。ErrorController
ErrorAttributes
可以用作自定义 .
如果要为新内容类型添加处理程序(默认设置是专门处理并为其他所有内容提供回退),这将特别有用。
为此,请 extend ,添加一个具有属性的公共方法,并创建一个新类型的 bean。BasicErrorController ErrorController text/html BasicErrorController @RequestMapping produces |
从 Spring Framework 6.0 开始,支持 RFC 7807 问题详细信息。
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
您还可以定义一个带注释的类,以自定义要针对特定控制器和/或异常类型返回的 JSON 文档,如以下示例所示:@ControllerAdvice
-
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 表示形式而不是表示形式。MyException
SomeController
MyErrorBody
ErrorAttributes
在某些情况下,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>
对于更复杂的映射,您还可以添加实现接口的 Bean,如以下示例所示:ErrorViewResolver
-
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 的应用程序,可以使用接口直接注册 。
此抽象直接与底层嵌入式 servlet 容器一起使用,即使您没有 Spring MVC 也有效。ErrorPageRegistrar
ErrorPages
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"))
}
}
如果注册的路径最终由 a 处理(这在一些非 Spring Web 框架中很常见,如 Jersey 和 Wicket),则必须显式注册为调度程序,如以下示例所示:ErrorPage Filter 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 使用其错误页面过滤器将具有错误状态的请求转发到相应的错误页面。 这是必要的,因为 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 配置不需要任何特定配置。全局 CORS 配置可以通过使用自定义方法注册 Bean 来定义,如以下示例所示:WebMvcConfigurer
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/**")
}
}
}
}
您可以根据需要定义任意数量的 Bean 来模块化路由器的定义。
如果您需要应用优先级,可以订购 Bean。RouterFunction |
财产 | DateTimeFormatter |
---|---|
|
|
|
|
|
|
如果应用程序打包为 jar,请不要使用该目录。
虽然这个目录是一个通用标准,但它只适用于战争打包,如果你生成一个 jar,大多数构建工具都会默默地忽略它。src/main/webapp |
如果使用 JBoss,则需要声明依赖项,而不是 .
否则,所有 Webjar 都解析为 .webjars-locator-jboss-vfs webjars-locator-core 404 |
由于为 Thymeleaf 和 FreeMarker 自动配置了 Thymeleaf 的链接,因此在运行时在模板中重写了指向资源的链接。
使用 JSP 时,应手动声明此过滤器。
目前不自动支持其他模板引擎,但可以使用自定义模板宏/帮助程序和 ResourceUrlProvider 的使用。ResourceUrlEncodingFilter |
|
使用 Bean 声明的端点 |
|
在 Bean 中声明的端点 |
|
欢迎页面支持 |
如果可能,应避免使用 JSP。 将它们与嵌入式 servlet 容器一起使用时,存在一些已知的限制。 |
根据应用程序的运行方式,IDE 可能会以不同的方式对类路径进行排序。 在 IDE 中从 main 方法运行应用程序的顺序与使用 Maven 或 Gradle 或从其打包的 jar 运行应用程序时的顺序不同。 这可能会导致 Spring Boot 无法找到预期的模板。 如果遇到此问题,可以在 IDE 中对类路径重新排序,以将模块的类和资源放在首位。 |
可以用作自定义 .
如果要为新内容类型添加处理程序(默认设置是专门处理并为其他所有内容提供回退),这将特别有用。
为此,请 extend ,添加一个具有属性的公共方法,并创建一个新类型的 bean。BasicErrorController ErrorController text/html BasicErrorController @RequestMapping produces |
如果注册的路径最终由 a 处理(这在一些非 Spring Web 框架中很常见,如 Jersey 和 Wicket),则必须显式注册为调度程序,如以下示例所示:ErrorPage Filter Filter ERROR |
JAX-RS 和泽西岛
如果您更喜欢 REST 端点的 JAX-RS 编程模型,那么可以使用其中一个可用的实现来代替 Spring MVC。Jersey 和 Apache CXF 开箱即用。
CXF 要求您在应用程序上下文中将其 OR 注册为 。
Jersey 有一些原生的 Spring 支持,因此我们还在 Spring Boot 中为它提供了自动配置支持,以及一个启动器。Servlet
Filter
@Bean
若要开始使用 Jersey,请将 作为依赖项包含在内,然后需要一种类型来注册所有终结点,如以下示例所示:spring-boot-starter-jersey
@Bean
ResourceConfig
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 |
对于更高级的定制,您还可以注册任意数量的实现 的 Bean。ResourceConfigCustomizer
所有已注册的终结点都应具有 HTTP 资源注释(和其他),如以下示例所示:@Components
@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";
}
}
由于 是 Spring ,它的生命周期由 Spring 管理,您可以使用注解来注入依赖关系,并使用注解来注入外部配置。
缺省情况下,Jersey servlet 已注册并映射到 。
您可以通过将 .Endpoint
@Component
@Autowired
@Value
/*
@ApplicationPath
ResourceConfig
默认情况下,Jersey 设置为类型为 的 servlet。
缺省情况下,Servlet 是延迟初始化的,但您可以通过设置 来自定义该行为。
您可以通过创建一个同名的 Bean 来禁用或覆盖该 Bean。
您还可以通过设置使用过滤器而不是 servlet(在这种情况下,要替换或覆盖的是 )。
筛选器有一个 ,您可以使用 进行设置。
使用 Jersey 作为过滤器时,必须存在一个 servlet 来处理 Jersey 未截获的任何请求。
如果应用程序不包含此类 Servlet,则可能需要通过设置为 来启用缺省 Servlet。
Servlet 和过滤器注册都可以被赋予 init 参数,用于指定属性映射。@Bean
ServletRegistrationBean
jerseyServletRegistration
spring.jersey.servlet.load-on-startup
spring.jersey.type=filter
@Bean
jerseyFilterRegistration
@Order
spring.jersey.filter.order
server.servlet.register-default-servlet
true
spring.jersey.init.*
Jersey 对扫描可执行存档的支持相当有限。
例如,它无法扫描在完全可执行的 jar 文件中找到的包中的端点,也无法在运行可执行的 war 文件时扫描端点。
为了避免此限制,不应使用该方法,而应使用该方法单独注册终结点,如前面的示例所示。WEB-INF/classes packages register |
嵌入式 Servlet 容器支持
对于 servlet 应用程序,Spring Boot 包括对嵌入式 Tomcat、Jetty 和 Undertow 服务器的支持。
大多数开发人员使用适当的“Starter”来获取完全配置的实例。
默认情况下,嵌入式服务器侦听端口上的 HTTP 请求。8080
Servlet、过滤器和侦听器
使用嵌入式 Servlet 容器时,可以通过使用 Spring Bean 或扫描 Servlet 组件来注册 Servlet 规范中的 Servlet、过滤器和所有侦听器(例如 )。HttpSessionListener
将 Servlet、过滤器和侦听器注册为 Spring Bean
任何作为 Spring Bean 的 、 或 servlet 实例都注册到嵌入式容器中。
如果要在配置期间引用您的值,这将特别方便。Servlet
Filter
*Listener
application.properties
缺省情况下,如果上下文仅包含单个 Servlet,则该 Servlet 将映射到 。
在多个 servlet Bean 的情况下,Bean 名称用作路径前缀。
筛选器映射到 。/
/*
如果基于约定的映射不够灵活,则可以使用 、 和 类进行完全控制。ServletRegistrationBean
FilterRegistrationBean
ServletListenerRegistrationBean
通常,不订购过滤豆是安全的。
如果需要特定的顺序,则应注释 with 或使其实现 。
不能通过用 注释 bean 方法来配置 a 的顺序。
如果无法更改要添加或实现的类,则必须为 定义 a 并使用该方法设置注册 Bean 的顺序。
避免配置读取请求正文的筛选器,因为它可能与应用程序的字符编码配置相悖。
如果 servlet 过滤器包装请求,则应为其配置小于或等于 的顺序。Filter
@Order
Ordered
Filter
@Order
Filter
@Order
Ordered
FilterRegistrationBean
Filter
setOrder(int)
Ordered.HIGHEST_PRECEDENCE
OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER
若要查看应用程序中每个的顺序,请为日志记录组 () 启用调试级别日志记录。
然后,将在启动时记录已注册过滤器的详细信息,包括其顺序和 URL 模式。Filter web logging.level.web=debug |
注册 Bean 时要小心,因为它们是在应用程序生命周期的早期初始化的。
如果您需要注册与其他 Bean 交互的 Bean,请考虑改用 DelegatingFilterProxyRegistrationBean 。Filter Filter |
Servlet 上下文初始化
嵌入式 servlet 容器不直接执行接口或 Spring 的接口。
这是一个有意的设计决策,旨在降低设计为在战争中运行的第三方库可能破坏 Spring Boot 应用程序的风险。jakarta.servlet.ServletContainerInitializer
org.springframework.web.WebApplicationInitializer
如果需要在 Spring Boot 应用程序中执行 servlet 上下文初始化,则应注册实现该接口的 Bean。
单一方法提供对 的访问,如有必要,可以很容易地用作现有 .org.springframework.boot.web.servlet.ServletContextInitializer
onStartup
ServletContext
WebApplicationInitializer
The ServletWebServerApplicationContext
在后台,Spring Boot 使用不同类型的嵌入式 servlet 容器支持。
这是一种特殊类型,它通过搜索单个 Bean 来引导自己。
通常为 、 或 已自动配置。ApplicationContext
ServletWebServerApplicationContext
WebApplicationContext
ServletWebServerFactory
TomcatServletWebServerFactory
JettyServletWebServerFactory
UndertowServletWebServerFactory
您通常不需要了解这些实现类。
大多数应用程序都是自动配置的,并且是代表您创建的。ApplicationContext ServletWebServerFactory |
在嵌入式容器设置中,设置为服务器启动的一部分,在应用程序上下文初始化期间发生。
因此,中的 Bean 无法可靠地使用 初始化。
解决此问题的一种方法是作为 Bean 的依赖项注入,并仅在需要时访问。
另一种方法是在服务器启动后使用回调。
这可以使用侦听如下内容来完成:ServletContext
ApplicationContext
ServletContext
ApplicationContext
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 属性配置常见的 servlet 容器设置。
通常,您将在 or 文件中定义属性。Environment
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 的 bean。SameSite
server.servlet.session.cookie.same-site
SessionRepository
例如,如果您希望会话 Cookie 的属性为 ,则可以将以下内容添加到您的 or 文件中:SameSite
None
application.properties
application.yaml
-
Properties
-
YAML
server.servlet.session.cookie.same-site=none
server:
servlet:
session:
cookie:
same-site: "none"
如果要更改添加到您的其他 cookie 上的属性,可以使用 .
传递 a 并可能返回一个值,或者 。SameSite
HttpServletResponse
CookieSameSiteSupplier
CookieSameSiteSupplier
Cookie
SameSite
null
您可以使用许多便利工厂和过滤方法来快速匹配特定的 Cookie。
例如,添加以下 bean 将自动对名称与正则表达式匹配的所有 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 容器自动映射到字符集。
每个容器都提供到字符集映射的默认区域设置,您应验证它们是否满足应用程序的需求。
如果不这样做,请使用 configuration 属性自定义映射,如以下示例所示:Accept-Language
server.servlet.encoding.mapping
-
Properties
-
YAML
server.servlet.encoding.mapping.ko=UTF-8
server:
servlet:
encoding:
mapping:
ko: "UTF-8"
在前面的示例中,(朝鲜语)区域设置已映射到 。
这相当于传统战争部署文件中的条目。ko
UTF-8
<locale-encoding-mapping-list>
web.xml
程序化定制
如果需要以编程方式配置嵌入式 servlet 容器,可以注册实现该接口的 Spring bean。 提供对 的访问,其中包括许多自定义 setter 方法。
以下示例演示如何以编程方式设置端口:WebServerFactoryCustomizer
WebServerFactoryCustomizer
ConfigurableServletWebServerFactory
-
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
,并且是专用变体,分别为 Tomcat、Jetty 和 Undertow 提供了额外的自定义设置器方法。
以下示例演示如何自定义以提供对特定于 Tomcat 的配置选项的访问:JettyServletWebServerFactory
UndertowServletWebServerFactory
ConfigurableServletWebServerFactory
TomcatServletWebServerFactory
-
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
对于需要从 扩展的更高级用例,您可以自己公开此类类型的 Bean。ServletWebServerFactory
为许多配置选项提供了 Setter。 如果您需要做一些更奇特的事情,还提供了几个受保护的方法“钩子”。 有关详细信息,请参阅源代码文档。
自动配置的定制工具仍应用于您的自定义工厂,因此请谨慎使用该选项。 |
若要查看应用程序中每个的顺序,请为日志记录组 () 启用调试级别日志记录。
然后,将在启动时记录已注册过滤器的详细信息,包括其顺序和 URL 模式。Filter web logging.level.web=debug |
注册 Bean 时要小心,因为它们是在应用程序生命周期的早期初始化的。
如果您需要注册与其他 Bean 交互的 Bean,请考虑改用 DelegatingFilterProxyRegistrationBean 。Filter Filter |
@ServletComponentScan 在独立容器中不起作用,在独立容器中,将改用容器的内置发现机制。 |
您通常不需要了解这些实现类。
大多数应用程序都是自动配置的,并且是代表您创建的。ApplicationContext ServletWebServerFactory |
有关完整列表,请参阅 ServerProperties 类。 |
自动配置的定制工具仍应用于您的自定义工厂,因此请谨慎使用该选项。 |