https://docs.spring.io/spring-boot/docs/3.2.0/reference/htmlsingle/#web.servlet
Spring Web MVC 框架
Spring Web MVC 框架(通常称为“Spring MVC”)是一个功能丰富的“模型 视图 控制器”Web 框架。Spring MVC 允许你创建特殊的 @Controller
或 @RestController
bean 来处理传入的 HTTP 请求。控制器中的方法通过使用 @RequestMapping
注解映射到 HTTP。
以下代码展示了一个典型的 @RestController
,它提供 JSON 数据:
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);
}
}
“WebMvc.fn”是一个功能变体,它将路由配置与实际处理请求的操作分开,如下所示:
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.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();
}
}
Spring MVC 是 Spring 框架的核心部分。
提示:可以根据需要定义任意数量的 RouterFunction
bean 来模块化路由器的定义。如果需要应用优先级,可以对 bean 进行排序。
Spring MVC 自动配置
Spring Boot 为 Spring MVC 提供了自动配置,这对于大多数应用程序而言非常有用。它取代了使用 @EnableWebMvc
的需求,并且这两个不能一起使用。除了 Spring MVC 的默认设置外,自动配置还提供了以下功能:
- 包含
ContentNegotiatingViewResolver
和BeanNameViewResolver
bean。 - 支持提供静态资源,包括支持 WebJars。
- 自动注册
Converter
、GenericConverter
和Formatter
bean。 - 支持
HttpMessageConverters
。 - 自动注册 MessageCodesResolver。
- 支持静态的
index.html
。 - 自动使用
ConfigurableWebBindingInitializer
bean。
如果你想保留这些 Spring Boot MVC 自定义,并进行更多 MVC 自定义(例如拦截器、格式化程序、视图控制器和其它功能),则可以添加你自己的类型为 WebMvcConfigurer
的 @Configuration
类,但不使用 @EnableWebMvc
。
如果你想提供 RequestMappingHandlerMapping
、RequestMappingHandlerAdapter
或 ExceptionHandlerExceptionResolver
的自定义实例,同时仍保留 Spring Boot MVC 的自定义设置,则可以声明一个类型为 WebMvcRegistrations
的 bean,并使用它来提供这些组件的自定义实例。这些自定义实例将接受 Spring MVC 的进一步初始化和配置。如果要参与(且希望)覆盖后续的处理,则应该使用 WebMvcConfigurer
。
如果你不想使用自动配置,并希望完全控制 Spring MVC,请添加你自己的带有 @EnableWebMvc
注解的 @Configuration
。另外,也可以添加你自己的带有 @Configuration
注解的 DelegatingWebMvcConfiguration
。
Spring MVC 转换服务(Conversion Service)
Spring MVC 使用的 ConversionService
与用于从 application.properties
或 application.yaml
文件中转换值的 ConversionService
不同。这意味着 Period
、Duration
和 DataSize
转换器不可用,并且 @DurationUnit
和 @DataSizeUnit
注解将被忽略。
如果你想自定义 Spring MVC 使用的 ConversionService
,可以提供一个带有 addFormatters
方法的 WebMvcConfigurer
bean。从该方法中,你可以注册任何你喜欢的转换器,或者可以委托给 ApplicationConversionService
上可用的静态方法。
还可以使用 spring.mvc.format.*
配置属性来定制转换。如果未进行配置,则使用以下默认值:
HttpMessageConverters
Spring MVC 使用 HttpMessageConverter
接口来转换 HTTP 请求和响应。它提供了合理的默认设置。例如,对象可以自动转换为 JSON(通过使用 Jackson 库)或 XML(如果可用,则使用 Jackson XML 扩展,否则使用 JAXB)。默认情况下,字符串以 UTF-8
编码。
如果你需要添加或自定义转换器,可以使用 Spring Boot 的 HttpMessageConverters
类,如下所示:
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);
}
}
上下文中存在的任何 HttpMessageConverter
bean 都会被添加到转换器列表中。你也可以用相同的方式覆盖默认转换器。
MessageCodesResolver
Spring MVC 有一个用于从绑定错误中生成错误代码以呈现错误消息的策略:MessageCodesResolver
。如果你设置了 spring.mvc.message-codes-resolver-format
属性为 PREFIX_ERROR_CODE
或 POSTFIX_ERROR_CODE
,Spring Boot 将为您创建一个。
静态内容
默认情况下,Spring Boot 从类路径中的名为 /static
(或 /public
或 /resources
或 /META-INF/resources
)的目录或从 ServletContext
的根目录中提供静态内容。它使用 Spring MVC 的 ResourceHttpRequestHandler
,因此你可以通过添加自己的 WebMvcConfigurer
并覆盖 addResourceHandlers
方法来修改该行为。
在独立的 Web 应用程序中,容器中的默认 servlet 未启用。可以通过使用 server.servlet.register-default-servlet
属性来启用它。
默认 servlet 作为回退机制,如果 Spring 决定不处理它,则从 ServletContext
的根目录提供内容。在大多数情况下,这不会发生(除非你修改了默认的 MVC 配置),因为 Spring 总是可以通过 DispatcherServlet
处理请求。
默认情况下,资源映射到 /**
,但你可以使用 spring.mvc.static-path-pattern
属性进行调整。例如,将所有资源重新定位到 /resources/**
可以按照以下方式实现:
spring.mvc.static-path-pattern=/resources/**
你还可以使用 spring.web.resources.static-locations
属性自定义静态资源位置(用目录位置的列表替换默认值)。Servlet 上下文的根路径,“/
”,也将自动作为位置添加。
除了前面提到的“标准”静态资源位置外,还对 Webjars 内容进行了特殊处理。默认情况下,如果以 Webjars 格式打包,则任何路径为 /webjars/**
的资源都从 jar 文件中提供。可以使用 spring.mvc.webjars-path-pattern
属性自定义路径。
提示:如果你的应用程序被打包为 jar 文件,请不要使用 src/main/webapp
目录。尽管此目录是一个常见的标准,但它仅适用于 war 打包,并且如果你生成 jar 文件,大多数构建工具都会静默忽略它。
Spring Boot 还支持 Spring MVC 提供的高级资源处理功能,允许使用诸如打破缓存的静态资源或使用 Webjars 的无版本 URL 等。
要使用 Webjars 的无版本 URL,请添加 webjars-locator-core
依赖项。然后声明你的 Webjar。以 jQuery 为例,添加 “/webjars/jquery/jquery.min.js
” 将导致 “/webjars/jquery/x.y.z/jquery.min.js
”,其中 x.y.z
是 Webjar 版本。
注意:如果你使用 JBoss,则需要声明 webjars-locator-jboss-vfs
依赖项,而不是 webjars-locator-core
。否则,所有 Webjars 都将解析为 404
。
要使用缓存打破功能,以下配置为所有静态资源配置了缓存打破解决方案,有效地在 URL 中添加了一个内容哈希,例如 <link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>
:
spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**
注意:由于自动配置了 ResourceUrlEncodingFilter
,因此可以在运行时在模板中重写资源链接,该过滤器适用于 Thymeleaf 和 FreeMarker。在使用 JSP 时,应手动声明此过滤器。其它模板引擎目前不自动支持,但可以通过自定义模板宏/帮助程序和 ResourceUrlProvider
的使用来支持。
当使用例如 JavaScript 模块加载器动态加载资源时,重命名文件不是一个选项。这就是为什么还支持其它策略,并且可以将它们组合在一起。“fixed”策略在 URL 中添加一个静态版本字符串,而不更改文件名,如下所示:
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
使用此配置,位于 “/js/lib/
” 下的 JavaScript 模块使用固定的版本策略(“/v12/js/lib/mymodule.js
”),而其它资源仍然使用内容版本(<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>
)。
欢迎页(Welcome Page)
Spring Boot 支持静态和模板化的欢迎页面。它首先在配置的静态内容位置中查找 index.html
文件。如果找不到,则查找index
模板。如果找到其中任何一个,它将被自动用作应用程序的欢迎页面。
这仅作为应用程序定义的实际索引路由的回退。顺序由 HandlerMapping
bean 的顺序定义,默认情况下如下:
自定义网站图标(Custom Favicon)
与其它静态资源一样,Spring Boot 检查配置的静态内容位置中是否存在 favicon.ico
。如果存在这样的文件,它将被自动用作应用程序的网站图标。
路径匹配和内容协商
Spring MVC 可以通过查看请求路径并将其与应用程序中定义的映射(例如,Controller
方法上的 @GetMapping
注解)进行匹配,将传入的 HTTP 请求映射到处理程序。
Spring Boot 默认选择禁用后缀模式匹配,这意味着像 “GET /projects/spring-boot.json
” 这样的请求将不会与 @GetMapping("/projects/spring-boot")
映射匹配。这被认为是 Spring MVC 应用程序的最佳实践。此功能在过去对于不发送适当的 “Accept” 请求头的 HTTP 客户端很有用;我们需要确保向客户端发送正确的内容类型。如今,内容协商更加可靠。
处理不始终发送正确的 “Accept” 请求头的 HTTP 客户端还有其它方法。我们不使用后缀匹配,而是可以使用查询参数来确保像 “GET /projects/spring-boot?format=json
” 这样的请求会映射到 @GetMapping("/projects/spring-boot")
:
spring.mvc.contentnegotiation.favor-parameter=true
或者,如果你更喜欢使用不同的参数名称:
spring.mvc.contentnegotiation.favor-parameter=true
spring.mvc.contentnegotiation.parameter-name=myparam
大多数标准媒体类型都开箱即用,但你也可以定义新的媒体类型:
spring.mvc.contentnegotiation.media-types.markdown=text/markdown
从 Spring Framework 5.3 开始,Spring MVC 支持两种将请求路径与控制器匹配的策略。默认情况下,Spring Boot 使用 PathPatternParser
策略。与 AntPathMatcher
策略相比,PathPatternParser
是一种经过优化的实现,但也有一些限制。PathPatternParser
限制了一些路径模式变体的使用。此外,它与配置 DispatcherServlet
的路径前缀(spring.mvc.servlet.path
)不兼容。
可以使用 spring.mvc.pathmatch.matching-strategy
配置属性来配置此策略,如下所示:
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
默认情况下,如果找不到处理请求的处理器,Spring MVC 将发送一个 404 Not Found 错误响应。要改为抛出 NoHandlerFoundException
,请将 configprop:spring.mvc.throw-exception-if-no-handler-found
设置为 true
。请注意,默认情况下,静态内容的提供会映射到 /**
,因此会为所有请求提供处理程序。要抛出 NoHandlerFoundException
,还必须将 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 为以下模板引擎提供了自动配置支持:
- FreeMarker
- Groovy
- Thymeleaf
- Mustache
提示:如果可能的话,应避免使用 JSP。在使用嵌入式 Servlet 容器时,JSP 存在一些已知的限制。
当你使用默认配置使用这些模板引擎之一时,你的模板将自动从src/main/resources/templates
中获取。
提示:根据你运行应用程序的方式,你的 IDE 可能会以不同的顺序排列类路径。从主方法在其 IDE 中运行应用程序时,类路径的顺序与使用 Maven 或 Gradle 运行应用程序或从打包的 jar 文件运行时的顺序不同。这可能导致 Spring Boot 无法找到预期的模板。如果你遇到这个问题,可以在 IDE 中重新排序类路径,将模块的类和资源放在前面。
错误处理
默认情况下,Spring Boot 提供了一个 /error
映射,它以合理的方式处理所有错误,并在 servlet 容器中注册为“global”错误页面。对于机器客户端,它会生成一个包含错误详细信息、HTTP 状态和异常消息的 JSON 响应。对于浏览器客户端,有一个“whitelabel”错误视图,它以 HTML 格式呈现相同的数据(要自定义它,请添加一个解析为error
的 View
)。
如果你想要自定义默认的错误处理行为,有许多 server.error
属性可以设置。
要完全替换默认行为,你可以实现 ErrorController
并注册该类型的 bean 定义,或者添加一个类型为 ErrorAttributes
的 bean 以使用现有机制但替换其内容。
提示:可以使用 BasicErrorController
作为自定义 ErrorController
的基类。特别是当你想为新的内容类型添加处理程序时(默认情况下,特别处理 text/html
并为其它所有内容提供备用),这特别有用。要做到这一点,请扩展 BasicErrorController
,添加一个带有 @RequestMapping
的公共方法,该方法具有 produces
属性,并创建你新类型的 bean。
从 Spring Framework 6.0 开始,支持FC 7807 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 文档,如下所示:
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;
}
}
在前面的示例中,如果 SomeController
中定义的控制器抛出了 MyException
,则使用 MyErrorBody
POJO 的 JSON 表示形式,而不是 ErrorAttributes
表示形式。
在某些情况下,控制器级别处理的错误不会被度量基础架构(metrics infrastructure)记录。应用程序可以通过将处理的异常设置为请求属性来确保这些异常被请求指标记录:
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
@Controller
public class MyController {
@ExceptionHandler(CustomException.class)
String handleCustomException(HttpServletRequest request, CustomException ex) {
request.setAttribute(ErrorAttributes.ERROR_ATTRIBUTE, ex);
return "errorView";
}
}
自定义错误页面
如果你想要为给定的状态码显示自定义的 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,如下所示:
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;
}
}
你还可以使用常规的 Spring MVC 功能,如 @ExceptionHandler
方法和@ControllerAdvice
。然后,ErrorController
捕获任何未处理的异常。
在 Spring MVC 之外映射错误页面
对于不使用 Spring MVC 的应用程序,你可以使用 ErrorPageRegistrar
接口直接注册 ErrorPages
。此抽象直接与底层嵌入式 servlet 容器一起工作,即使你没有 Spring MVC 的 DispatcherServlet
也能正常工作。
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"));
}
}
注意:如果你注册了一个ErrorPage
,其路径最终由Filter
(如在Jersey和Wicket等非Spring web框架中常见)处理,那么Filter
必须显式地注册为ERROR
分发器,如下所示:
import java.util.EnumSet;
import jakarta.servlet.DispatcherType;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class MyFilterConfiguration {
@Bean
public FilterRegistrationBean<MyFilter> myFilter() {
FilterRegistrationBean<MyFilter> registration = new FilterRegistrationBean<>(new MyFilter());
// ...
registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
return registration;
}
}
请注意,默认的FilterRegistrationBean
不包括ERROR
分发器类型。
WAR部署中的错误处理
当部署到servlet容器时,Spring Boot使用其错误页面过滤器将带有错误状态的请求转发到适当的错误页面。这是必要的,因为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应用程序中使用带有@CrossOrigin
注解的控制器方法CORS配置不需要任何特定的配置。可以通过注册一个带有自定义addCorsMappings(CorsRegistry)
方法的WebMvcConfigurer
bean来定义全局CORS配置,如下所示:
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/**");
}
};
}
}
JAX-RS 和 Jersey
如果你更喜欢使用JAX-RS编程模型来创建REST端点,则可以使用可用的实现之一来替代Spring MVC。Jersey和Apache CXF开箱即用,效果非常好。CXF要求你将Servlet
或Filter
注册为应用程序上下文中的@Bean
。Jersey具有一些原生的Spring支持,因此在Spring Boot中为其提供了自动配置支持,以及一个启动器。
要开始使用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
等)的@Components
,如下所示:
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import org.springframework.stereotype.Component;
@Component
@Path("/hello")
public class MyEndpoint {
@GET
public String message() {
return "Hello";
}
}
由于Endpoint
是一个Spring @Component
,因此其生命周期由Spring管理,你可以使用@Autowired
注解注入依赖项,并使用@Value
注解注入外部配置。默认情况下,Jersey servlet已注册并映射到/*
。你可以通过将@ApplicationPath
添加到ResourceConfig
来更改映射。
默认情况下,Jersey是作为名为jerseyServletRegistration
的ServletRegistrationBean
类型的@Bean
中的servlet设置的。默认情况下,servlet会延迟初始化,但你可以通过设置spring.jersey.servlet.load-on-startup
来定制此行为。你可以通过创建具有相同名称的bean来禁用或覆盖该bean。还可以通过设置spring.jersey.type=filter
来使用过滤器代替servlet(在这种情况下,要替换或覆盖的@Bean
是jerseyFilterRegistration
)。过滤器具有@Order
,你可以使用spring.jersey.filter.order
进行设置。当使用Jersey作为过滤器时,必须存在一个servlet来处理Jersey未拦截的任何请求。如果你的应用程序不包含这样的servlet,则可能通过设置server.servlet.register-default-servlet
为true
来启用默认servlet。servlet和过滤器的注册都可以通过使用spring.jersey.init.*
来指定属性映射来给定初始化参数。
嵌入式Servlet容器支持
对于servlet应用程序,Spring Boot包括对嵌入式Tomcat,Jetty和Undertow服务器的支持。大多数开发人员使用适当的“启动器”来获取完全配置的实例。默认情况下,嵌入式服务器在端口8080
上侦听HTTP请求。
Servlet,Filter和Listener
当使用嵌入式servlet容器时,可以通过使用Spring bean或扫描servlet组件来注册servlet规范中的servlet,filter和所有listener(如HttpSessionListener
)。
将Servlet,Filter和Listener注册为Spring Bean
任何Servlet
,Filter
或servlet *Listener
实例,如果是Spring bean,都会注册到嵌入式容器中。这在配置过程中,如果想在application.properties
中引用某个值时,会特别方便。
默认情况下,如果上下文中仅包含一个Servlet,则将其映射到/
。在多个servlet bean的情况下,bean名称用作路径前缀。过滤器映射到/*
。
如果基于约定的映射不够灵活,可以使用ServletRegistrationBean
,FilterRegistrationBean
和ServletListenerRegistrationBean
类进行完全控制。
不将过滤器bean排序通常是安全的。如果需要特定的顺序,应该使用@Order
注解Filter
或实现Ordered
。不能通过在其bean方法上使用@Order
来配置Filter
的顺序。如果无法更改Filter
类以添加@Order
或实现Ordered
,则必须为Filter
定义一个FilterRegistrationBean
,并使用setOrder(int)
方法设置注册bean的顺序。避免配置以Ordered.HIGHEST_PRECEDENCE
读取请求体的过滤器,因为这可能会违反你应用程序的字符编码配置。如果servlet过滤器包装了请求,则应该将其配置为小于或等于OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER
的顺序。
提示:要查看应用程序中每个Filter
的顺序,请为Web日志组启用调试级别日志记录(logging.level.web=debug
)。然后,将在启动时记录已注册过滤器的详细信息,包括其顺序和URL模式。
注意:在注册Filter
bean时要小心,因为它们在应用程序生命周期中非常早地被初始化。如果需要注册一个与其它bean交互的Filter
,考虑使用DelegatingFilterProxyRegistrationBean
。
servlet上下文初始化
嵌入式servlet容器不会直接执行jakarta.servlet.ServletContainerInitializer
接口或Spring的org.springframework.web.WebApplicationInitializer
接口。这是一个有意的设计决策,旨在降低设计为在war内部运行的第三方库可能破坏Spring Boot应用程序的风险。
如果需要在Spring Boot应用程序中进行servlet上下文初始化,则应注册一个实现org.springframework.boot.web.servlet.ServletContextInitializer
接口的bean。单个onStartup
方法提供了对ServletContext
的访问,如果需要,可以轻松用作现有WebApplicationInitializer
的适配器。
扫描Servlet,Filter和Listener
当使用嵌入式容器时,可以通过使用@ServletComponentScan
启用对用@WebServlet
,@WebFilter
和@WebListener
注解的类的自动注册。
提示:在独立容器中,@ServletComponentScan
不起作用,而是使用容器的内置发现机制。
ServletWebServerApplicationContext
在底层,Spring Boot使用不同类型的ApplicationContext
来支持嵌入式servlet容器。ServletWebServerApplicationContext
是一种特殊的WebApplicationContext
类型,它通过搜索单个ServletWebServerFactory
bean来引导自身。通常,已经自动配置了TomcatServletWebServerFactory
,JettyServletWebServerFactory
或UndertowServletWebServerFactory
。
注意:你通常不需要了解这些实现类。大多数应用程序都是自动配置的,将为你创建适当的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容器设置。通常,你会在application.properties
或application.yaml
文件中定义这些属性。
常见的服务器设置包括:
- 网络设置:用于传入HTTP请求的监听端口(
server.port
)、要绑定的接口地址(server.address
)等。 - 会话设置:会话是否持久(
server.servlet.session.persistent
)、会话超时时间(server.servlet.session.timeout
)、会话数据存储位置(server.servlet.session.store-dir
)以及会话cookie配置(server.servlet.session.cookie.*
)等。 - 错误管理:错误页面的位置(
server.error.path
)等。 - SSL
- HTTP压缩
Spring Boot尽可能多地公开常见设置,但这并不总是可能的。对于这些情况,专用的命名空间提供了针对服务器的特定自定义。例如,可以使用嵌入式servlet容器的特定功能来配置访问日志。
SameSite cookie
web浏览器可以使用SameSite
cookie属性来控制是否在跨站点请求中提交cookie,以及如何提交。该属性对于现代web浏览器特别重要,因为现代浏览器已经开始改变在缺少该属性时使用的默认值。
如果想更改会话cookie的SameSite
属性,可以使用server.servlet.session.cookie.same-site
属性。该属性受自动配置的Tomcat、Jetty和Undertow服务器的支持。它还用于配置基于Spring Session servlet的SessionRepository
bean。
例如,如果希望会话cookie的SameSite
属性为None
,可以在application.properties
或application.yaml
文件中添加以下内容:
server.servlet.session.cookie.same-site=none
如果想更改添加到HttpServletResponse
中的其它cookie的SameSite
属性,可以使用CookieSameSiteSupplier
。将Cookie传递给CookieSameSiteSupplier
,可以返回SameSite
值,或者返回null
。
可以使用许多方便的工厂和过滤方法来快速匹配特定的cookie。例如,添加以下bean将自动为名称与正则表达式myapp.*
匹配的所有cookie应用Lax
的SameSite
。
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.*");
}
}
字符编码
用于请求和响应处理的嵌入式servlet容器的字符编码行为可以通过server.servlet.encoding.*
配置属性进行配置。
当请求的Accept-Language
头指示了请求的区域(locale)设置时,servlet容器将自动将其映射到字符集。每个容器都提供默认的locale到字符集的映射,你应该验证它们是否满足你的应用程序需求。当它们不满足时,请使用server.servlet.encoding.mapping
配置属性来自定义映射,如下所示:
server.servlet.encoding.mapping.ko=UTF-8
在前面的示例中,已将ko
(朝鲜语)区域设置映射到UTF-8
。这相当于在传统war部署的web.xml
文件中的<locale-encoding-mapping-list>
条目。
程序化定制
如果你需要以编程方式配置嵌入式servlet容器,则可以注册一个实现WebServerFactoryCustomizer
接口的Spring bean。WebServerFactoryCustomizer
提供了对ConfigurableServletWebServerFactory
的访问,其中包括许多自定义setter方法。以下示例以编程方式设置端口:
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);
}
}
TomcatServletWebServerFactory
,JettyServletWebServerFactory
和UndertowServletWebServerFactory
是ConfigurableServletWebServerFactory
的专用变体,它们分别为Tomcat,Jetty和Undertow提供了额外的自定义setter方法。以下示例显示了如何自定义TomcatServletWebServerFactory
,该工厂提供了对特定于Tomcat的配置选项的访问:
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()));
}
}
直接自定义ConfigurableServletWebServerFactory
对于需要你从ServletWebServerFactory
继承的更高级使用场景,你可以自己公开这种类型的bean。
为许多配置选项提供了setter。如果你需要执行更复杂的操作,还提供了几个受保护的“钩子”方法。
注意:自动配置的customizer仍应用于你的自定义工厂,因此请谨慎使用该选项。
JSP 限制
当运行使用嵌入式servlet容器(并且打包为可执行存档文件)的Spring Boot应用程序时,JSP支持有一些限制。
- 如果使用war打包,那么对于Jetty和Tomcat,它应该可以工作。使用
java -jar
启动的可执行war将可以工作,并且也可以部署到任何标准容器中。使用可执行jar时不支持JSP。 - Undertow不支持JSP。
- 创建自定义的
error.jsp
页面不会覆盖错误处理的默认视图。应该使用自定义错误页面代替。