28. 开发 Web 应用程序

Spring Boot 非常适合 Web 应用程序开发。您可以使用嵌入式 Tomcat,Jetty,Undertow 或 Netty 创建独立的 HTTP 服务器。大多数 Web 应用程序都使用spring-boot-starter-web模块来快速启动和运行。您还可以选择使用spring-boot-starter-webflux模块来构建响应式 Web 应用程序。

如果尚未开发 Spring Boot Web 应用程序,则可以遵循“ Hello World!”。 * Getting started *部分中的示例。

28.1“ Spring Web MVC 框架”

Spring Web MVC 框架(通常简称为“ Spring MVC”)是一个丰富的“模型视图控制器” Web 框架。 Spring MVC 使您可以创建特殊的@Controller@RestController bean 来处理传入的 HTTP 请求。控制器中的方法通过使用@RequestMappingCommentsMap 到 HTTP。

以下代码显示了提供 JSON 数据的典型@RestController

@RestController
@RequestMapping(value="/users")
public class MyRestController {

	@RequestMapping(value="/{user}", method=RequestMethod.GET)
	public User getUser(@PathVariable Long user) {
		// ...
	}

	@RequestMapping(value="/{user}/customers", method=RequestMethod.GET)
	List<Customer> getUserCustomers(@PathVariable Long user) {
		// ...
	}

	@RequestMapping(value="/{user}", method=RequestMethod.DELETE)
	public User deleteUser(@PathVariable Long user) {
		// ...
	}

}

Spring MVC 是核心 Spring Framework 的一部分,有关详细信息,请参见reference documentationspring.io/guides提供了一些涵盖 Spring MVC 的指南。

28.1.1 Spring MVC 自动配置

Spring Boot 为 Spring MVC 提供了自动配置,可与大多数应用程序完美配合。

自动配置在 Spring 的默认值之上添加了以下功能:

  • 包括ContentNegotiatingViewResolverBeanNameViewResolverbean。

  • 支持提供静态资源,包括对 WebJars 的支持(覆盖本文档后面)。

  • 自动注册ConverterGenericConverterFormatter bean。

  • 支持HttpMessageConverters(包含本文档后面)。

  • 自动注册MessageCodesResolver(已发现本文档后面)。

  • 静态index.html支持。

  • 自定义Favicon支持(包含本文档后面)。

  • 自动使用ConfigurableWebBindingInitializer bean(包含本文档后面)。

如果您想保留 Spring Boot MVC 功能,并且想要添加其他MVC configuration(拦截器,格式化程序,视图控制器和其他功能),则可以添加自己的@Configuration类,类型为WebMvcConfigurer,但是 没有 @EnableWebMvc。如果希望提供RequestMappingHandlerMappingRequestMappingHandlerAdapterExceptionHandlerExceptionResolver的自定义实例,则可以声明WebMvcRegistrationsAdapter实例以提供此类组件。

如果要完全控制 Spring MVC,则可以添加自己的@Configuration并以@EnableWebMvcComments。

28.1.2 HttpMessageConverters

Spring MVC 使用HttpMessageConverter接口转换 HTTP 请求和响应。开箱即用中包含明智的默认设置。例如,可以将对象自动转换为 JSON(通过使用 Jackson 库)或 XML(通过使用 Jackson XML 扩展(如果可用)或通过使用 JAXB(如果 Jackson XML 扩展不可用))。默认情况下,字符串以UTF-8编码。

如果需要添加或定制转换器,则可以使用 Spring Boot 的HttpMessageConverters类,如以下 Lists 所示:

import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.*;

@Configuration
public class MyConfiguration {

	@Bean
	public HttpMessageConverters customConverters() {
		HttpMessageConverter<?> additional = ...
		HttpMessageConverter<?> another = ...
		return new HttpMessageConverters(additional, another);
	}

}

上下文中存在的任何HttpMessageConverter bean 都将添加到转换器列表中。您也可以用相同的方法覆盖默认转换器。

28.1.3 自定义 JSON 序列化器和反序列化器

如果使用 Jackson 来序列化和反序列化 JSON 数据,则可能要编写自己的JsonSerializerJsonDeserializer类。自定义序列化器通常是通过模块在 Jackson 注册,但是 Spring Boot 提供了@JsonComponentComments,这使得直接注册 Spring Bean 更加容易。

您可以直接在JsonSerializerJsonDeserializer实现中使用@JsonComponentComments。您还可以在包含序列化器/反序列化器作为内部类的类上使用它,如以下示例所示:

import java.io.*;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import org.springframework.boot.jackson.*;

@JsonComponent
public class Example {

	public static class Serializer extends JsonSerializer<SomeObject> {
		// ...
	}

	public static class Deserializer extends JsonDeserializer<SomeObject> {
		// ...
	}

}

ApplicationContext中的所有@JsonComponent bean 都会自动向 Jackson 进行注册。因为@JsonComponent是用@Component进行元 Comments 的,所以通常的组件扫描规则适用。

Spring Boot 还提供了JsonObjectSerializerJsonObjectDeserializerBase Class,这些 Base Class 在序列化对象时为标准 Jackson 版本提供了有用的替代方法。有关详细信息,请参见 Javadoc 中的JsonObjectSerializerJsonObjectDeserializer

28.1.4 MessageCodesResolver

Spring MVC 有一种生成错误代码的策略,该错误代码用于从绑定错误MessageCodesResolver渲染错误消息。如果设置spring.mvc.message-codes-resolver.format属性PREFIX_ERROR_CODEPOSTFIX_ERROR_CODE,Spring Boot 会为您创建一个(请参见DefaultMessageCodesResolver.Format中的枚举)。

28.1.5 静态内容

默认情况下,Spring Boot 从 Classpath 中名为/static(或/public/resources/META-INF/resources)的目录或ServletContext的根目录中提供静态内容。它使用 Spring MVC 中的ResourceHttpRequestHandler,因此您可以通过添加自己的WebMvcConfigurer并覆盖addResourceHandlers方法来修改该行为。

在独立的 Web 应用程序中,还启用了容器中的默认 servlet,并将其用作后备,如果 Spring 决定不处理ServletContext的根,则从ServletContext的根开始提供内容。在大多数情况下,这不会发生(除非您修改默认的 MVC 配置),因为 Spring 始终可以通过DispatcherServlet处理请求。

默认情况下,资源 Map 在/**上,但是您可以使用spring.mvc.static-path-pattern属性对其进行调整。例如,将所有资源重定位到/resources/**可以实现如下:

spring.mvc.static-path-pattern=/resources/**

您还可以使用spring.resources.static-locations属性来自定义静态资源位置(用目录位置列表替换默认值)。根 Servlet 上下文路径"/"也会自动添加为位置。

除了前面提到的“标准”静态资源位置以外,还对Webjars content进行了特殊处理。如果 jar 文件以 Webjars 格式打包,则从 jar 文件提供带有/webjars/**路径的任何资源。

Tip

如果您的应用程序打包为 jar,则不要使用src/main/webapp目录。尽管此目录是一个通用标准,但它仅在 war 打包中有效,并且在生成 jar 时,大多数构建工具都将其忽略。

Spring Boot 还支持 Spring MVC 提供的高级资源处理功能,允许使用案例,例如缓存清除静态资源或对 Webjars 使用版本无关的 URL。

要对 Webjar 使用版本无关的 URL,请添加webjars-locator-core依赖项。然后声明您的 Webjar。以 jQuery 为例,在"/webjars/jquery/x.y.z/jquery.min.js"中添加"/webjars/jquery/jquery.min.js"结果。其中x.y.z是 Webjar 版本。

Note

如果使用 JBoss,则需要声明webjars-locator-jboss-vfs依赖性而不是webjars-locator-core。否则,所有 Webjar 都解析为404

要使用缓存清除,以下配置为所有静态资源配置了缓存清除解决方案,从而有效地在 URL 中添加了内容哈希,例如<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>

spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**

Note

得益于为 Thymeleaf 和 FreeMarker 自动配置的ResourceUrlEncodingFilter,在运行时将资源链接重写为模板。使用 JSP 时,您应该手动声明此过滤器。当前不自动支持其他模板引擎,但可以与自定义模板宏/帮助器一起使用,并可以使用ResourceUrlProvider

例如,当使用 JavaScript 模块加载器动态加载资源时,不能重命名文件。这就是为什么其他策略也受支持并且可以组合的原因。 “固定”策略在 URL 中添加静态版本字符串,而不会更改文件名,如以下示例所示:

spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**
spring.resources.chain.strategy.fixed.enabled=true
spring.resources.chain.strategy.fixed.paths=/js/lib/
spring.resources.chain.strategy.fixed.version=v12

通过这种配置,位于"/js/lib/"下的 JavaScript 模块使用固定的版本控制策略("/v12/js/lib/mymodule.js"),而其他资源仍使用内容版本(<link href="/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css"/>)。

有关更多受支持的选项,请参见ResourceProperties

Tip

此功能已在专用的blog post和 Spring Framework 的reference documentation中进行了详细描述。

28.1.6 欢迎页面

Spring Boot 支持静态和模板欢迎页面。它首先在配置的静态内容位置中查找index.html文件。如果未找到,则它将寻找index模板。如果找到任何一个,它将自动用作应用程序的欢迎页面。

28.1.7 自定义图标

Spring Boot 在配置的静态内容位置和 Classpath 的根目录(按此 Sequences)中查找favicon.ico。如果存在这样的文件,它将自动用作应用程序的收藏夹图标。

28.1.8 路径匹配和内容协商

Spring MVC 可以通过查看请求路径并将其匹配到应用程序中定义的 Map(例如 Controller 方法上的@GetMappingComments),将传入的 HTTP 请求 Map 到处理程序。

Spring Boot 默认选择禁用后缀模式匹配,这意味着诸如"GET /projects/spring-boot.json"之类的请求将不会与@GetMapping("/projects/spring-boot")Map 相匹配。这被视为Spring MVC 应用程序的最佳实践。过去,此功能主要用于未发送正确的“ Accept”请求 Headers 的 HTTP Client 端。我们需要确保将正确的 Content Type 发送给 Client 端。如今,内容协商已变得更加可靠。

还有其他处理 HTTP Client 端的方法,这些方法不能始终发送正确的“ Accept”请求 Headers。除了使用后缀匹配,我们还可以使用查询参数来确保将"GET /projects/spring-boot?format=json"之类的请求 Map 到@GetMapping("/projects/spring-boot")

spring.mvc.contentnegotiation.favor-parameter=true

# We can change the parameter name, which is "format" by default:
# spring.mvc.contentnegotiation.parameter-name=myparam

# We can also register additional file extensions/media types with:
spring.mvc.contentnegotiation.media-types.markdown=text/markdown

如果您了解了注意事项,但仍希望您的应用程序使用后缀模式匹配,则需要以下配置:

spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-suffix-pattern=true

另外,与其打开所有后缀模式,不如只支持注册的后缀模式,这是更安全的:

spring.mvc.contentnegotiation.favor-path-extension=true
spring.mvc.pathmatch.use-registered-suffix-pattern=true

# You can also register additional file extensions/media types with:
# spring.mvc.contentnegotiation.media-types.adoc=text/asciidoc

28.1.9 ConfigurableWebBindingInitializer

Spring MVC 使用WebBindingInitializer来为特定请求初始化WebDataBinder。如果创建自己的ConfigurableWebBindingInitializer @Bean,Spring Boot 会自动将 Spring MVC 配置为使用它。

28.1.10 模板引擎

除了 REST Web 服务之外,您还可以使用 Spring MVC 来提供动态 HTML 内容。 Spring MVC 支持各种模板技术,包括 Thymeleaf,FreeMarker 和 JSP。同样,许多其他模板引擎包括他们自己的 Spring MVC 集成。

Spring Boot 包括对以下模板引擎的自动配置支持:

Tip

如果可能,应避免使用 JSP。与嵌入式 servlet 容器一起使用时,有多个known limitations

当您使用默认配置的这些模板引擎之一时,将从src/main/resources/templates自动拾取模板。

Tip

根据您运行应用程序的方式,IntelliJ IDEA 对 Classpath 的排序方式不同。与使用 Maven 或 Gradle 或从打包的 jar 运行应用程序时,从 IDE 的 Main 方法运行应用程序的 Sequences 会有所不同。这可能会导致 Spring Boot 无法在 Classpath 上找到模板。如果遇到此问题,则可以在 IDE 中重新排序 Classpath,以首先放置模块的类和资源。另外,您可以配置模板前缀以搜索 Classpath 上的每个templates目录,如下所示:classpath*:/templates/

28.1.11 错误处理

默认情况下,Spring Boot 提供了一个/errorMap,可以明智地处理所有错误,并且已在 servlet 容器中注册为“全局”错误页面。对于机器 Client 端,它将生成 JSON 响应,其中包含错误,HTTP 状态和异常消息的详细信息。对于浏览器 Client 端,存在一个“ whitelabel”错误视图,该视图以 HTML 格式渲染相同的数据(要对其进行自定义,请添加解析为errorView)。要完全替换默认行为,可以实现ErrorController并注册该类型的 Bean 定义,或添加ErrorAttributes类型的 Bean 以使用现有机制但替换其内容。

Tip

BasicErrorController可用作自定义ErrorController的 Base Class。如果要为新的 Content Type 添加处理程序(默认是专门处理text/html并为其他所有内容提供后备功能),则此功能特别有用。为此,扩展BasicErrorController,添加具有produces属性的@RequestMapping的公共方法,并创建新类型的 bean。

您还可以定义带有@ControllerAdviceComments 的类,以自定义 JSON 文档以针对特定的控制器和/或异常类型返回,如以下示例所示:

@ControllerAdvice(basePackageClasses = AcmeController.class)
public class AcmeControllerAdvice extends ResponseEntityExceptionHandler {

	@ExceptionHandler(YourException.class)
	@ResponseBody
	ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable ex) {
		HttpStatus status = getStatus(request);
		return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status);
	}

	private HttpStatus getStatus(HttpServletRequest request) {
		Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
		if (statusCode == null) {
			return HttpStatus.INTERNAL_SERVER_ERROR;
		}
		return HttpStatus.valueOf(statusCode);
	}

}

在前面的示例中,如果在与AcmeController相同的程序包中定义的控制器抛出YourException,则使用CustomErrorType POJO 的 JSON 表示代替ErrorAttributes表示。

自定义错误页面

如果要显示给定状态代码的自定义 HTML 错误页面,可以将文件添加到/error文件夹。错误页面可以是静态 HTML(即添加到任何静态资源文件夹下),也可以使用模板来构建。文件名应为确切的状态代码或系列掩码。

例如,要将404Map 到静态 HTML 文件,您的文件夹结构如下:

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

要使用 FreeMarker 模板 Map 所有5xx错误,您的文件夹结构如下:

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.ftl
             +- <other templates>

对于更复杂的 Map,还可以添加实现ErrorViewResolver接口的 bean,如以下示例所示:

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
		return ...
	}

}

您还可以使用常规的 Spring MVC 功能,例如@ExceptionHandler methods@ControllerAdviceErrorController然后接收任何未处理的异常。

Map Spring MVC 之外的错误页面

对于不使用 Spring MVC 的应用程序,可以使用ErrorPageRegistrar接口直接注册ErrorPages。此抽象直接与基础嵌入式 servlet 容器一起使用,即使您没有 Spring MVC DispatcherServlet,也可以使用。

@Bean
public ErrorPageRegistrar errorPageRegistrar(){
	return new MyErrorPageRegistrar();
}

// ...

private static class MyErrorPageRegistrar implements ErrorPageRegistrar {

	@Override
	public void registerErrorPages(ErrorPageRegistry registry) {
		registry.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/400"));
	}

}

Note

如果您注册的ErrorPage的路径最终由Filter处理(这在某些非 Spring Web 框架(如 Jersey 和 Wicket)中很常见),则必须将Filter明确注册为ERROR调度程序,如图所示。下面的例子:

@Bean
public FilterRegistrationBean myFilter() {
	FilterRegistrationBean registration = new FilterRegistrationBean();
	registration.setFilter(new MyFilter());
	...
	registration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));
	return registration;
}

请注意,默认的FilterRegistrationBean不包括ERROR调度程序类型。

注意:Spring Boot 部署到 servlet 容器时,将使用其错误页面过滤器将具有错误状态的请求转发到相应的错误页面。如果尚未提交响应,则只能将请求转发到正确的错误页面。缺省情况下,WebSphere Application Server 8.0 和更高版本在成功完成 servlet 的服务方法后提交响应。您应该通过将com.ibm.ws.webcontainer.invokeFlushAfterService设置为false来禁用此行为。

12 年 12 月 28 日 Spring HATEOAS

如果您开发使用超媒体的 RESTful API,Spring Boot 将为 Spring HATEOAS 提供自动配置,该配置可与大多数应用程序很好地配合使用。自动配置取代了使用@EnableHypermediaSupport的需要,并注册了许多 Bean 以简化基于超媒体的应用程序的构建,包括LinkDiscoverers(用于 Client 端支持)和ObjectMapper,这些ObjectMapper被配置为将响应正确地编组为所需的表示形式。 ObjectMapper是通过设置各种spring.jackson.*属性来定制的,或者通过设置Jackson2ObjectMapperBuilder bean 来定制(如果存在)。

您可以使用@EnableHypermediaSupport来控制 Spring HATEOAS 的配置。请注意,这样做会禁用前面介绍的ObjectMapper自定义。

28.1.13 CORS 支持

跨域资源共享(CORS)是由most browsers实现的W3C specification,可让您灵活地指定授权哪种类型的跨域请求,而不是使用一些安全性和功能不强的方法(例如 IFRAME 或 JSONP)。

从 4.2 版本开始,Spring MVC supports CORS。在 Spring Boot 应用程序中使用控制器方法 CORS 配置@CrossOrigin注解不需要任何特定的配置。可以通过使用自定义的addCorsMappings(CorsRegistry)方法注册WebMvcConfigurer bean 来定义全局 CORS 配置,如以下示例所示:

@Configuration
public class MyConfiguration {

	@Bean
	public WebMvcConfigurer corsConfigurer() {
		return new WebMvcConfigurer() {
			@Override
			public void addCorsMappings(CorsRegistry registry) {
				registry.addMapping("/api/**");
			}
		};
	}
}

28.2“ Spring WebFlux 框架”

Spring WebFlux 是 Spring Framework 5.0 中引入的新的响应式 Web 框架。与 Spring MVC 不同,它不需要 Servlet API,是完全异步和非阻塞的,并通过反应堆项目实现Reactive Streams规范。

Spring WebFlux 有两种形式:功能性的和基于 Comments 的。基于 Comments 的模型非常类似于 Spring MVC 模型,如以下示例所示:

@RestController
@RequestMapping("/users")
public class MyRestController {

	@GetMapping("/{user}")
	public Mono<User> getUser(@PathVariable Long user) {
		// ...
	}

	@GetMapping("/{user}/customers")
	public Flux<Customer> getUserCustomers(@PathVariable Long user) {
		// ...
	}

	@DeleteMapping("/{user}")
	public Mono<User> deleteUser(@PathVariable Long user) {
		// ...
	}

}

功能变体“ WebFlux.fn”将路由配置与请求的实际处理分开,如以下示例所示:

@Configuration
public class RoutingConfiguration {

	@Bean
	public RouterFunction<ServerResponse> monoRouterFunction(UserHandler userHandler) {
		return route(GET("/{user}").and(accept(APPLICATION_JSON)), userHandler::getUser)
				.andRoute(GET("/{user}/customers").and(accept(APPLICATION_JSON)), userHandler::getUserCustomers)
				.andRoute(DELETE("/{user}").and(accept(APPLICATION_JSON)), userHandler::deleteUser);
	}

}

@Component
public class UserHandler {

	public Mono<ServerResponse> getUser(ServerRequest request) {
		// ...
	}

	public Mono<ServerResponse> getUserCustomers(ServerRequest request) {
		// ...
	}

	public Mono<ServerResponse> deleteUser(ServerRequest request) {
		// ...
	}
}

WebFlux 是 Spring Framework 的一部分,其reference documentation中提供了详细信息。

Tip

您可以根据需要定义尽可能多的RouterFunction bean,以对 Router 的定义进行模块化。如果需要应用优先级,可以 Order Bean。

首先,将spring-boot-starter-webflux模块添加到您的应用程序中。

Note

在应用程序中同时添加spring-boot-starter-webspring-boot-starter-webflux模块会导致 Spring Boot 自动配置 Spring MVC,而不是 WebFlux。之所以选择这种行为,是因为许多 Spring 开发人员将spring-boot-starter-webflux添加到他们的 Spring MVC 应用程序中以使用 ReactiveWebClient。您仍然可以通过将选定的应用程序类型设置为SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)来强制执行选择。

28.2.1 Spring WebFlux 自动配置

Spring Boot 为 Spring WebFlux 提供了自动配置,可与大多数应用程序完美配合。

自动配置在 Spring 的默认值之上添加了以下功能:

  • HttpMessageReaderHttpMessageWriter实例(说明本文档后面)配置编解码器。

  • 支持服务静态资源,包括对 WebJars 的支持(描述为本文档后面)。

如果您想保留 Spring Boot WebFlux 功能,并且想要添加其他WebFlux configuration,则可以添加自己的@Configuration类,类型为WebFluxConfigurer,但 没有 @EnableWebFlux

如果要完全控制 Spring WebFlux,则可以添加带有@EnableWebFluxComments 的自己的@Configuration

28.2.2 带有 HttpMessageReaders 和 HttpMessageWriters 的 HTTP 编解码器

Spring WebFlux 使用HttpMessageReaderHttpMessageWriter接口转换 HTTP 请求和响应。通过查看 Classpath 中可用的库,将它们配置为CodecConfigurer以具有合理的默认值。

Spring Boot 通过使用CodecCustomizer实例应用进一步的自定义。例如,将spring.jackson.*个配置密钥应用于 Jackson 编解码器。

如果需要添加或自定义编解码器,则可以创建自定义CodecCustomizer组件,如以下示例所示:

import org.springframework.boot.web.codec.CodecCustomizer;

@Configuration
public class MyConfiguration {

	@Bean
	public CodecCustomizer myCodecCustomizer() {
		return codecConfigurer -> {
			// ...
		}
	}

}

您还可以使用Boot 的自定义 JSON 序列化器和反序列化器

28.2.3 静态内容

默认情况下,Spring Boot 从 Classpath 中名为/static(或/public/resources/META-INF/resources)的目录中提供静态内容。它使用 Spring WebFlux 中的ResourceWebHandler,以便您可以通过添加自己的WebFluxConfigurer并覆盖addResourceHandlers方法来修改该行为。

默认情况下,资源 Map 在/**上,但是您可以通过设置spring.webflux.static-path-pattern属性对其进行调整。例如,将所有资源重定位到/resources/**可以实现如下:

spring.webflux.static-path-pattern=/resources/**

您还可以使用spring.resources.static-locations自定义静态资源位置。这样做会将默认值替换为目录位置列表。如果这样做,默认的欢迎页面检测将切换到您的自定义位置。因此,如果启动时您的任何位置都存在index.html,则它是应用程序的主页。

除了前面列出的“标准”静态资源位置之外,Webjars content也是一种特殊情况。如果 jar 文件以 Webjars 格式打包,则从 jar 文件提供带有/webjars/**路径的任何资源。

Tip

Spring WebFlux 应用程序不严格依赖 Servlet API,因此不能将它们部署为 war 文件,也不使用src/main/webapp目录。

28.2.4 模板引擎

除了 REST Web 服务之外,您还可以使用 Spring WebFlux 来提供动态 HTML 内容。 Spring WebFlux 支持多种模板技术,包括 Thymeleaf,FreeMarker 和 Mustache。

Spring Boot 包括对以下模板引擎的自动配置支持:

当您使用默认配置的这些模板引擎之一时,将从src/main/resources/templates自动拾取模板。

28.2.5 错误处理

Spring Boot 提供了一个WebExceptionHandler,可以明智地处理所有错误。它在处理 Sequences 中的位置紧靠 WebFlux 提供的处理程序之前,该处理程序被认为是最后一个。对于机器 Client 端,它将生成 JSON 响应,其中包含错误,HTTP 状态和异常消息的详细信息。对于浏览器 Client 端,有一个“ whitelabel”错误处理程序,以 HTML 格式渲染相同的数据。您还可以提供自己的 HTML 模板来显示错误(请参见next section)。

定制此功能的第一步通常涉及使用现有机制,但替换或增加错误内容。为此,您可以添加ErrorAttributes类型的 bean。

要更改错误处理行为,可以实现ErrorWebExceptionHandler并注册该类型的 bean 定义。由于WebExceptionHandler的级别很低,因此 Spring Boot 还提供了一个方便的AbstractErrorWebExceptionHandler,让您以 WebFlux 功能方式处理错误,如以下示例所示:

public class CustomErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

	// Define constructor here

	@Override
	protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {

		return RouterFunctions
				.route(aPredicate, aHandler)
				.andRoute(anotherPredicate, anotherHandler);
	}

}

要获得更完整的图片,您还可以直接继承DefaultErrorWebExceptionHandler并重写特定方法。

自定义错误页面

如果要显示给定状态代码的自定义 HTML 错误页面,可以将文件添加到/error文件夹。错误页面可以是静态 HTML(即添加到任何静态资源文件夹下),也可以使用模板构建。文件名应为确切的状态代码或系列掩码。

例如,要将404Map 到静态 HTML 文件,您的文件夹结构如下:

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- public/
             +- error/
             |   +- 404.html
             +- <other public assets>

要使用 Mustache 模板 Map 所有5xx错误,您的文件夹结构如下:

src/
 +- main/
     +- java/
     |   + <source code>
     +- resources/
         +- templates/
             +- error/
             |   +- 5xx.mustache
             +- <other templates>

28.2.6 网页过滤器

Spring WebFlux 提供了一个WebFilter接口,可以实现该接口来过滤 HTTP 请求-响应交换。在应用程序上下文中找到的WebFilter bean 将自动用于过滤每次交换。

在过滤器 Sequences 很重要的地方,它们可以实现Ordered或用@OrderComments。 Spring Boot 自动配置可能会为您配置 Web 过滤器。这样做时,将使用下表中显示的 Sequences:

Web FilterOrder
MetricsWebFilterOrdered.HIGHEST_PRECEDENCE + 1
WebFilterChainProxy(Spring Security 性)-100
HttpTraceWebFilterOrdered.LOWEST_PRECEDENCE - 10

28.3 JAX-RS 和 Jersey

如果您更喜欢 REST 端点的 JAX-RS 编程模型,则可以使用可用的实现之一来代替 Spring MVC。 JerseyApache CXF开箱即用。 CXF 要求您在应用程序上下文中将其ServletFilter注册为@Bean。Jersey(Jersey)有一些本机 Spring 支持,因此我们在 Spring Boot 中还与启动程序一起为其提供了自动配置支持。

要开始使用 Jersey,请将spring-boot-starter-jersey作为依赖项,然后需要一个ResourceConfig类型的@Bean,在其中注册所有端点,如以下示例所示:

@Component
public class JerseyConfig extends ResourceConfig {

	public JerseyConfig() {
		register(Endpoint.class);
	}

}

Warning

Jersey 对扫描可执行归档文件的支持非常有限。例如,在运行可执行的 war 文件时,它无法扫描在完全可执行的 jar 文件WEB-INF/classes中找到的程序包中的端点。为避免此限制,不应使用packages方法,并且应使用register方法分别注册端点,如前面的示例所示。

对于更高级的定制,您还可以注册实现ResourceConfigCustomizer的任意数量的 bean。

所有注册的端点都应为@Components,并带有 HTTP 资源 Comments(@GET和其他 Comments),如以下示例所示:

@Component
@Path("/hello")
public class Endpoint {

	@GET
	public String message() {
		return "Hello";
	}

}

由于Endpoint是 Spring @Component,其生命周期由 Spring Management,因此您可以使用@Autowired注解注入依赖项,并使用@Value注解注入外部配置。默认情况下,Jersey servlet 已注册并 Map 到/*。您可以通过将@ApplicationPath添加到ResourceConfig来更改 Map。

默认情况下,Jersey 在名为jerseyServletRegistrationServletRegistrationBean类型的@Bean中设置为 Servlet。默认情况下,该 Servlet 延迟初始化,但是您可以通过设置spring.jersey.servlet.load-on-startup来自定义该行为。您可以通过使用相同的名称创建自己的一个来禁用或覆盖该 bean。您还可以通过设置spring.jersey.type=filter(在这种情况下,要替换或覆盖的@BeanjerseyFilterRegistration)来使用过滤器而不是 Servlet。过滤器具有@Order,您可以使用spring.jersey.filter.order进行设置。可以使用spring.jersey.init.*指定属性 Map,从而为 servlet 和过滤器注册都赋予 init 参数。

有一个Jersey sample,以便您可以了解如何进行设置。

28.4 嵌入式 Servlet 容器支持

Spring Boot 包含对嵌入式TomcatJettyUndertow服务器的支持。大多数开发人员使用适当的“启动器”来获取完全配置的实例。默认情况下,嵌入式服务器在端口8080上侦听 HTTP 请求。

Warning

如果选择在CentOS上使用 Tomcat,请注意,默认情况下,将使用一个临时目录来存储编译的 JSP,文件上载等等。您的应用程序运行时,此目录可能被tmpwatch删除,从而导致失败。为避免这种情况,您可能需要自定义tmpwatch配置,以使tomcat.*目录不会被删除,或配置server.tomcat.basedir,以使嵌入式 Tomcat 使用其他位置。

28.4.1 Servlet,过滤器和侦听器

使用嵌入式 Servlet 容器时,可以通过使用 Spring bean 或扫描 Servlet 组件来注册 Servlet 规范中的 Servlet,过滤器和所有侦听器(例如HttpSessionListener)。

将 Servlet,过滤器和侦听器注册为 Spring Bean

任何作为 Spring bean 的ServletFilter或 servlet *Listener实例都向嵌入式容器注册。如果要在配置过程中引用application.properties中的值,这可能特别方便。

默认情况下,如果上下文仅包含单个 Servlet,则将其 Map 到/。对于多个 servlet bean,bean 名称用作路径前缀。过滤器 Map 到/*

如果基于约定的 Map 不够灵活,则可以使用ServletRegistrationBeanFilterRegistrationBeanServletListenerRegistrationBean类进行完全控制。

Spring Boot 附带了许多可能定义 Filter bean 的自动配置。以下是过滤器及其各自 Sequences 的一些示例(较低的 Sequences 值表示较高的优先级):

Servlet FilterOrder
OrderedCharacterEncodingFilterOrdered.HIGHEST_PRECEDENCE
WebMvcMetricsFilterOrdered.HIGHEST_PRECEDENCE + 1
ErrorPageFilterOrdered.HIGHEST_PRECEDENCE + 1
HttpTraceFilterOrdered.LOWEST_PRECEDENCE - 10

通常可以使无序滤 bean 处于无序状态。

如果需要特定的 Sequences,则应避免配置一个在Ordered.HIGHEST_PRECEDENCE读取请求正文的过滤器,因为它可能与应用程序的字符编码配置不符。如果 Servlet 过滤器包装了请求,则应以小于或等于OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER的 Sequences 对其进行配置。

28.4.2 Servlet 上下文初始化

嵌入式 Servlet 容器不会直接执行 Servlet 3.0 javax.servlet.ServletContainerInitializer接口或 Spring 的org.springframework.web.WebApplicationInitializer接口。这是一个有意设计的决定,目的是减少旨在在 War 中运行的第三方库可能破坏 Spring Boot 应用程序的风险。

如果需要在 Spring Boot 应用程序中执行 servlet 上下文初始化,则应该注册一个实现org.springframework.boot.web.servlet.ServletContextInitializer接口的 bean。单个onStartup方法提供对ServletContext的访问,并且在必要时可以轻松地用作现有WebApplicationInitializer的适配器。

扫描 Servlet,过滤器和侦听器

使用嵌入式容器时,可以通过使用@ServletComponentScan来启用自动注册带有@WebServlet@WebFilter@WebListenerComments 的类。

Tip

@ServletComponentScan在独立容器中无效,而是使用该容器的内置发现机制。

28.4.3 ServletWebServerApplicationContext

在后台,Spring Boot 使用另一种类型的ApplicationContext来支持嵌入式 servlet 容器。 ServletWebServerApplicationContextWebApplicationContext的一种特殊类型,它通过搜索单个ServletWebServerFactory bean 来进行自我引导。通常TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory已被自动配置。

Note

通常,您不需要了解这些实现类。大多数应用程序都是自动配置的,并且代表您创建了相应的ApplicationContextServletWebServerFactory

28.4.4 自定义嵌入式 Servlet 容器

可以使用 Spring Environment属性来配置常见的 servlet 容器设置。通常,您将在application.properties文件中定义属性。

常用服务器设置包括:

  • 网络设置:侦听传入 HTTP 请求的端口(server.port),绑定到server.address的接口地址,等等。

  • 会话设置:会话是否持久(server.servlet.session.persistence),会话超时(server.servlet.session.timeout),会话数据的位置(server.servlet.session.store-dir)和会话 cookie 配置(server.servlet.session.cookie.*)。

  • 错误 Management:错误页面的位置(server.error.path),依此类推。

  • SSL

  • HTTP compression

Spring Boot 尝试尽可能多地公开通用设置,但这并不总是可能的。在这种情况下,专用名称空间提供服务器特定的自定义项(请参见server.tomcatserver.undertow)。例如,access logs可以配置有嵌入式 Servlet 容器的特定功能。

Tip

有关完整列表,请参见ServerProperties类。

Programmatic Customization

如果需要以编程方式配置嵌入式 servlet 容器,则可以注册一个实现WebServerFactoryCustomizer接口的 Spring bean。 WebServerFactoryCustomizer提供对ConfigurableServletWebServerFactory的访问,其中包括许多自定义设置方法。以下示例显示以编程方式设置端口:

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.stereotype.Component;

@Component
public class CustomizationBean implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> {

	@Override
	public void customize(ConfigurableServletWebServerFactory server) {
		server.setPort(9000);
	}

}

Note

TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactoryConfigurableServletWebServerFactory的专用变体,分别具有针对 Tomcat,Jetty 和 Undertow 的其他自定义设置方法。

直接自定义 ConfigurableServletWebServerFactory

如果上述定制技术太有限,则可以自己注册TomcatServletWebServerFactoryJettyServletWebServerFactoryUndertowServletWebServerFactory bean。

@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
	TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
	factory.setPort(9000);
	factory.setSessionTimeout(10, TimeUnit.MINUTES);
	factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html"));
	return factory;
}

提供了许多配置选项的设置器。如果您需要做一些更奇特的操作,还提供了几种受保护的方法“钩子”。有关详情,请参见源代码文档

28.4.5 JSP 限制

当运行使用嵌入式 servlet 容器(并打包为可执行归档文件)的 Spring Boot 应用程序时,JSP 支持存在一些限制。

  • 对于 Jetty 和 Tomcat,如果使用 War 包装,它应该可以工作。与java -jar一起启动时,可执行的 War 将起作用,并且也可部署到任何标准容器中。使用可执行 jar 时,不支持 JSP。

  • Undertow 不支持 JSP。

  • 创建自定义error.jsp页面不会覆盖error handling的默认视图。应该改用自定义错误页面

有一个JSP sample,以便您可以了解如何进行设置。

28.5 嵌入式反应式服务器支持

Spring Boot 包含对以下嵌入式反应式 Web 服务器的支持:Reactor Netty,Tomcat,Jetty 和 Undertow。大多数开发人员使用适当的“启动器”来获取完全配置的实例。默认情况下,嵌入式服务器在端口 8080 上侦听 HTTP 请求。

28.6 Reactive 服务器资源配置

当自动配置 Reactor Netty 或 Jetty 服务器时,Spring Boot 将创建特定的 bean,这些 bean 将为服务器实例提供 HTTP 资源:ReactorResourceFactoryJettyResourceFactory

默认情况下,在以下情况下,这些资源还将与 Reactor Netty 和 Jetty Client 端共享,以实现最佳性能:

  • 服务器和 Client 端使用相同的技术

  • Client 端实例是使用 Spring Boot 自动配置的WebClient.Builder bean 构建的

通过提供自定义的ReactorResourceFactoryJettyResourceFactory bean,开发人员可以覆盖 Jetty 和 Reactor Netty 的资源配置-这将同时应用于 Client 端和服务器。

您可以在WebClient 运行时部分中了解有关 Client 端资源配置的更多信息。