Web

1. Servlet Web 应用

如果你想构建基于servlet的网页应用,可以利用Spring Boot对Spring MVC或Jersey的自动配置功能。spring-doc.cadn.net.cn

1.1. “Spring Web MVC 框架”

Spring Web MVC 框架(通常称为“Spring MVC”)是一个丰富的“模型视图控制器”网络框架。 春季MVC让你创建特殊技能@Controller@RestController用来处理来电的HTTP请求。 控制器里的方法通过以下方式映射到 HTTP@RequestMapping附注。spring-doc.cadn.net.cn

以下代码显示了典型的@RestController该数据提供 JSON 数据:spring-doc.cadn.net.cn

Java
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);
    }

}
Kotlin
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”功能变体将路由配置与实际请求处理分离,如下示例所示:spring-doc.cadn.net.cn

Java
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();
    }

}
Kotlin
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
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();
    }

}
Kotlin
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 框架的一部分,详细信息可在参考文档中提供。 spring.io/guides 上也有几本涵盖春季MVC的指南。spring-doc.cadn.net.cn

你可以定义多少路由器功能豆子,你喜欢模块化路由器的定义。 如果需要优先顺序,可以订购Beans。

1.1.1. SpringMVC自动配置

Spring Boot 为 Spring MVC 提供了自动配置,适用于大多数应用程序。 它取代了@EnableWebMvc而且两者不能同时使用。 除了 Spring MVC 的默认设置外,自动配置还提供以下功能:spring-doc.cadn.net.cn

如果你想保留那些 Spring Boot MVC 自定义,并做更多 MVC 自定义(拦截器、格式化器、视图控制器及其他功能),你可以添加自己的@Configuration类型类别WebMvcConfigurer没有 @EnableWebMvc.spring-doc.cadn.net.cn

如果你想提供自定义实例请求映射处理映射,RequestMappingHandlerAdapterExceptionHandlerExceptionResolver,同时保留Spring Boot MVC的自定义,你可以声明一个类型的豆子WebMvc注册并用它来提供这些组件的自定义实例。 自定义实例将由 Spring MVC 进一步初始化和配置。 为了参与并如愿可覆盖该后续处理,一个WebMvcConfigurer应该使用。spring-doc.cadn.net.cn

如果你不想使用自动配置,想完全控制 Spring MVC,可以自己添加@Configuration注释为@EnableWebMvc. 或者,你也可以添加自己的@Configuration-注释DelegatingWebMvcConfiguration如 Javadoc 所述@EnableWebMvc.spring-doc.cadn.net.cn

1.1.2. 春季MVC转换服务

春季MVC使用不同的方式转换服务转换到用于将你的值转换的那个application.propertiesapplication.yaml文件。 意思是时期,期间数据大小没有转换器,且@DurationUnit@DataSizeUnit注释将被忽略。spring-doc.cadn.net.cn

如果你想自定义转换服务Spring MVC使用的话,你可以提供WebMvcConfigurer带有addFormatters方法。 通过这种方法,你可以注册任何你喜欢的转换器,或者你可以委派给 Statics 方法。ApplicationConversionService.spring-doc.cadn.net.cn

转换也可以通过以下方式进行定制spring.mvc.format.*配置属性。 未配置时,使用以下默认值:spring-doc.cadn.net.cn

属性 DateTimeFormatter

spring.mvc.format.datespring-doc.cadn.net.cn

ofLocalizedDate(FormatStyle.SHORT)spring-doc.cadn.net.cn

spring.mvc.format.timespring-doc.cadn.net.cn

ofLocalizedTime(FormatStyle.SHORT)spring-doc.cadn.net.cn

spring.mvc.format.date-timespring-doc.cadn.net.cn

ofLocalizedDateTime(FormatStyle.SHORT)spring-doc.cadn.net.cn

1.1.3. HttpMessage转换器

春季MVC使用以下HttpMessage转换器用于转换HTTP请求和响应的接口。 默认默认设置是开箱即用的。 例如,对象可以自动转换为JSON(使用Jackson库)或XML(如果有Jackson XML扩展,或如果没有Jackson XML扩展则使用JAXB)。 默认情况下,字符串编码为UTF-8.spring-doc.cadn.net.cn

如果你需要添加或自定义转换器,可以用Spring Boot的HttpMessage转换器如以下列表所示:spring-doc.cadn.net.cn

Java
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);
    }

}
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)
class MyHttpMessageConvertersConfiguration {

    @Bean
    fun customConverters(): HttpMessageConverters {
        val additional: HttpMessageConverter<*> = AdditionalHttpMessageConverter()
        val another: HttpMessageConverter<*> = AnotherHttpMessageConverter()
        return HttpMessageConverters(additional, another)
    }

}

任何HttpMessage转换器上下文中存在的豆子会被加入转换器列表。 你也可以用同样的方式覆盖默认转换器。spring-doc.cadn.net.cn

1.1.4. 消息代码解析器

Spring MVC 有一种策略用于生成错误代码,用于从绑定错误中渲染错误消息:MessageCodesResolver. 如果你设置spring.mvc.message-codes-resolver-format属性PREFIX_ERROR_CODEPOSTFIX_ERROR_CODE,Spring Boot 为你创建一个(参见DefaultMessageCodesResolver.Format).spring-doc.cadn.net.cn

1.1.5. 静态内容

默认情况下,Spring Boot 从名为/静态的(或/公共/资源/元补强/资源)在类路径中或从ServletContext. 它使用ResourceHttpRequestHandler从Spring MVC中删除,这样你可以通过添加自己的行为来修改该行为WebMvcConfigurer并且覆盖addResourceHandlers方法。spring-doc.cadn.net.cn

在独立的网页应用中,容器中的默认 servlet 不会被启用。 可以通过以下方式启用server.servlet.register-default-servlet财产。spring-doc.cadn.net.cn

默认的servlet作为后备,从根节点提供内容ServletContext如果Spring决定不处理的话。 大多数情况下,这种情况不会发生(除非你修改默认的MVC配置),因为Spring总能通过调度器服务.spring-doc.cadn.net.cn

默认情况下,资源映射在 ,但你可以用/**spring.mvc.static-path-pattern财产。 例如,将所有资源重新定位到/资源/**可以实现如下:spring-doc.cadn.net.cn

性能
spring.mvc.static-path-pattern=/resources/**
Yaml
spring:
  mvc:
    static-path-pattern: "/resources/**"

你也可以通过使用spring.web.resources.static-locations属性(用目录位置列表替换默认值)。 根 servlet 上下文路径 也会自动作为位置添加。"/"spring-doc.cadn.net.cn

除了前面提到的“标准”静态资源位置外,Webjars 内容还有一个特殊情况。 默认情况下,任何路径在 的资源/webjars/ **如果 jar 文件以 Webjars 格式打包,则 是从中提供。 路径可以通过以下方式进行自定义Spring.mvc.webjars-path-pattern财产。spring-doc.cadn.net.cn

不要使用src/main/webapp如果你的应用是打包成jar的,目录。 虽然该目录是通用标准,但它适用于战争包装,且大多数构建工具在生成jar时会默默认忽略。

Spring Boot 还支持 Spring MVC 提供的高级资源处理功能,允许使用如缓存破坏静态资源或使用版本无关的 Webjar URL 等用例。spring-doc.cadn.net.cn

要使用版本无关的Webjar URL,请添加Webjars-定位器-核心Dependency。 然后声明你的Webjar。 以jQuery为例,添加“/webjars/jquery/jquery.min.js”结果如下“/webjars/jquery/x.y.z/jquery.min.js”哪里X.Y.Z是Webjar版本。spring-doc.cadn.net.cn

如果你用JBoss,你需要声明Webjars-locator-jboss-vfs依赖性代替Webjars-定位器-核心. 否则,所有Webjar都以404.

要使用缓存破坏,以下配置为所有静态资源配置缓存破坏解决方案,实际上增加了内容哈希,例如<link href=“/css/spring-2a2d595e6ed9a0b24f027f2b63b134d6.css”/>,在URL中:spring-doc.cadn.net.cn

性能
spring.web.resources.chain.strategy.content.enabled=true
spring.web.resources.chain.strategy.content.paths=/**
Yaml
spring:
  web:
    resources:
      chain:
        strategy:
          content:
            enabled: true
            paths: "/**"
资源链接在运行时会在模板中重写,这得益于ResourceUrlEncodingFilter该系统自动配置为Thymeleaf 和 FreeMarker。 使用JSP时应手动声明这个过滤器。 其他模板引擎目前不自动支持,但可以通过自定义模板宏/辅助工具和使用ResourceUrlProvider.

例如,在使用JavaScript模块加载器动态加载资源时,无法重命名文件。 这就是为什么其他策略也被支持并可以组合使用。 “固定”策略是在不更改文件名的情况下,在URL中添加静态版本字符串,如下示例所示:spring-doc.cadn.net.cn

性能
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
Yaml
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”/>).spring-doc.cadn.net.cn

WebProperties.资源更多支持的选项。spring-doc.cadn.net.cn

这一功能在专门的博客文章和 Spring Framework 的参考文档中都有详细描述。spring-doc.cadn.net.cn

1.1.6. 欢迎页面

Spring Boot 支持静态和模板欢迎页。 它首先寻找一个index.html文件位于配置好的静态内容位置。 如果找不到,则会寻找指数模板。 如果找到其中任何一个,它会自动作为申请的欢迎页面使用。spring-doc.cadn.net.cn

1.1.7. 定制 Favicon

与其他静态资源一样,Spring Boot 会检查favicon.ico在配置好的静态内容位置。 如果存在这样的文件,它会自动作为应用程序的动态编辑器使用。spring-doc.cadn.net.cn

1.1.8. 路径匹配与内容协商

Spring MVC 可以通过查看请求路径并将其与你应用程序中定义的映射匹配,将收到的 HTTP 请求映射到处理器上(例如,@GetMapping控制器方法的注释)。spring-doc.cadn.net.cn

Spring Boot 默认选择禁用后缀模式匹配,这意味着“获取/项目/spring-boot.json”不会匹配到@GetMapping(“/projects/spring-boot”)映射。 这被视为春季MVC应用的最佳实践。 这一功能过去主要适用于未发送正确“接受”请求头的 HTTP 客户端;我们需要确保向客户发送正确的内容类型。 如今,内容协商更加可靠。spring-doc.cadn.net.cn

还有其他方法可以处理那些不稳定发送正确“接受”请求头的 HTTP 客户端。 我们可以用查询参数来确保请求“GET /projects/spring-boot?format=json”将映射到@GetMapping(“/projects/spring-boot”):spring-doc.cadn.net.cn

性能
spring.mvc.contentnegotiation.favor-parameter=true
Yaml
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true

或者如果你喜欢用不同的参数名:spring-doc.cadn.net.cn

性能
spring.mvc.contentnegotiation.favor-parameter=true
spring.mvc.contentnegotiation.parameter-name=myparam
Yaml
spring:
  mvc:
    contentnegotiation:
      favor-parameter: true
      parameter-name: "myparam"

大多数标准媒体类型开箱即用,但你也可以定义新的:spring-doc.cadn.net.cn

性能
spring.mvc.contentnegotiation.media-types.markdown=text/markdown
Yaml
spring:
  mvc:
    contentnegotiation:
      media-types:
        markdown: "text/markdown"

截至 Spring Framework 5.3,Spring MVC 支持两种将请求路径匹配到控制器的策略。 默认情况下,Spring Boot 使用路径模式解析器策略。路径模式解析器优化实现,但相比蚁径匹配器策略。路径模式解析器限制某些路径模式变体的使用。 它也无法与配置调度器服务带有路径前缀(spring.mvc.servlet.path).spring-doc.cadn.net.cn

该策略可以通过以下方式进行配置spring.mvc.pathmatch.matching-strategy配置性质,如下示例所示:spring-doc.cadn.net.cn

性能
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
Yaml
spring:
  mvc:
    pathmatch:
      matching-strategy: "ant-path-matcher"

默认情况下,如果请求未找到处理程序,Spring MVC会发送404“未找到”错误响应。 拥有一个未处理者发现异常Thrown Instead, Set Configprop:Spring.mvc.Throw-Exception-if-no-handler-found totrue. 请注意,默认情况下,静态内容的提供映射为 ,因此会为所有请求提供一个处理程序。 对于一个/**未处理者发现异常要被掷出,你还必须设置spring.mvc.static-path-pattern变为更具体的值,例如/资源/**或集合spring.web.resources.add-mappingsfalse完全禁用静态内容的提供。spring-doc.cadn.net.cn

1.1.9. ConfigurableWebBindingInitializer

Spring MVC 使用WebBindingInitializer以初始化一个WebDataBinder针对某个特别的请求。 如果你自己创建ConfigurableWebBindingInitializer @BeanSpring Boot 会自动配置 Spring MVC 以使用该功能。spring-doc.cadn.net.cn

1.1.10. 模板引擎

除了 REST Web 服务,你还可以用 Spring MVC 来提供动态 HTML 内容。 Spring MVC 支持多种模板技术,包括 Thymeleaf、FreeMark 和 JSP。 此外,许多其他模板引擎也包含自己的 Spring MVC 集成。spring-doc.cadn.net.cn

Spring Boot 支持以下模板引擎的自动配置:spring-doc.cadn.net.cn

如果可能,应避免使用JSP。 在与嵌入式 servlet 容器一起使用时,存在一些已知的限制

当你使用这些模板引擎中的默认配置时,模板会自动从以下获取src/main/resources/模板.spring-doc.cadn.net.cn

根据你运行应用的方式,IDE可能会对类路径的排序有所不同。 在IDE中用主方法运行应用,排序与使用Maven、Gradle或其打包jar运行时不同。 这可能导致 Spring Boot 找不到预期的模板。 如果你遇到这个问题,可以在IDE中重新排序类路径,把模块的类和资源放在最前面。

1.1.11. 错误处理

默认情况下,Spring Boot 提供/错误映射以合理方式处理所有错误,并在 servlet 容器中注册为“全局”错误页面。 对于机器客户端,它会生成包含错误信息、HTTP 状态和异常消息的 JSON 响应。 对于浏览器客户端,有一个“白标”错误视图,它以HTML格式渲染相同的数据(要自定义,可以添加视图该 结算为错误).spring-doc.cadn.net.cn

有若干server.error如果你想自定义默认的错误处理行为,可以设置这些属性。 详见附录中的“服务器属性”部分。spring-doc.cadn.net.cn

要完全替换默认行为,你可以实现错误控制器并注册该类型的豆定义,或添加一个类型的豆错误属性使用现有机制但替换内容物。spring-doc.cadn.net.cn

基础错误控制器可以作为自定义的基类使用错误控制器. 如果你想为新内容类型添加处理程序(默认是处理),这尤其有用文本/HTML特别是为其他所有事情提供备选方案)。 要做到这一点,请延长基础错误控制器,添加一个具有@RequestMapping生产属性,然后创建你新类型的豆子。

截至 Spring Framework 6.0,支持 RFC 7807 问题详情。 Spring MVC 可以生成自定义错误消息,并配备application/problem+json媒体类型,比如:spring-doc.cadn.net.cn

{
  "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.enabledtrue.spring-doc.cadn.net.cn

你也可以定义一个注释为@ControllerAdvice自定义 JSON 文档以返回特定控制器和/或异常类型,如下示例所示:spring-doc.cadn.net.cn

Java
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;
    }

}
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])
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
    }

}

在前述例子中,如果我的例外由定义在同一包中某个控制者,一个 JSON 表示我的错误身体使用 POJO 代替错误属性表示法。spring-doc.cadn.net.cn

在某些情况下,控制器级别处理的错误不会被度量基础设施记录。 应用程序可以通过将处理异常设置为请求属性来确保这些异常与请求指标一起记录:spring-doc.cadn.net.cn

Java
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";
    }

}
Kotlin
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
class MyController {

    @ExceptionHandler(CustomException::class)
    fun handleCustomException(request: HttpServletRequest, ex: CustomException?): String {
        request.setAttribute(ErrorAttributes.ERROR_ATTRIBUTE, ex)
        return "errorView"
    }

}
自定义错误页面

如果你想为某个状态码显示自定义的 HTML 错误页面,你可以向/错误目录。 错误页面可以是静态 HTML(即添加到任何静态资源目录下),也可以通过模板构建。 文件名称应为准确的状态码或系列掩码。spring-doc.cadn.net.cn

例如,映射404对于静态HTML文件,你的目录结构如下:spring-doc.cadn.net.cn

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

映射所有5xx错误,使用 FreeMarker 模板时,你的目录结构如下:spring-doc.cadn.net.cn

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

对于更复杂的映射,你还可以添加实现ErrorViewResolver如下示例所示的接口:spring-doc.cadn.net.cn

Java
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;
    }

}
Kotlin
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. 这错误控制器然后拾取任何未处理的异常。spring-doc.cadn.net.cn

Spring MVC 以外的映射错误页面

对于不使用 Spring MVC 的应用程序,可以使用ErrorPage注册商直接注册的接口错误页面. 这种抽象直接与底层嵌入的 servlet 容器工作,即使没有 Spring MVC 也能运行调度器服务.spring-doc.cadn.net.cn

Java
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"));
    }

}
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)
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(这在一些非 Spring 的 Web 框架中很常见,比如 Jersey 和 Wicket),然后Filter必须明确注册为错误调度员,如下例所示:
Java
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;
    }

}
Kotlin
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不包括错误调度员类型。spring-doc.cadn.net.cn

WAR部署中的错误处理

当部署到servlet容器时,Spring Boot会使用错误页面过滤器将带有错误状态的请求转发到相应的错误页面。 这是必要的,因为servlet规范没有提供注册错误页的API。 根据你部署战争文件的容器和应用所用技术,可能需要一些额外的配置。spring-doc.cadn.net.cn

错误页面过滤器只能在响应尚未提交时将请求转发到正确的错误页面。 默认情况下,WebSphere Application Server 8.0及以后版本在成功完成servlet服务方法时提交响应。 你应该通过设置来禁用这种行为com.ibm.ws.webcontainer.invokeFlushAfterServicefalse.spring-doc.cadn.net.cn

1.1.12. CORS支持。

跨源资源共享(CORS)是W3C规范,大多数浏览器实现它允许灵活地指定授权的跨域请求类型,而不是使用一些安全性较低、功能较弱的方法,如IFRAME或JSONP。spring-doc.cadn.net.cn

从4.2版本起,Spring MVC支持CORS。 使用控制器方法,CORS配置@CrossOriginSpring Boot 应用中的注释不需要特定的配置。全局CORS配置可以通过注册WebMvcConfigurer带有定制的豆子addCorsMappings(CorsRegistry)如下例所示:spring-doc.cadn.net.cn

Java
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/**");
            }

        };
    }

}
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)
class MyCorsConfiguration {

    @Bean
    fun corsConfigurer(): WebMvcConfigurer {
        return object : WebMvcConfigurer {
            override fun addCorsMappings(registry: CorsRegistry) {
                registry.addMapping("/api/**")
            }
        }
    }

}

1.2. JAX-RS与Jersey号

如果你更喜欢 JAX-RS 编程模型用于 REST 端点,可以使用现有的实现之一,而不是 Spring MVC。JerseyApache CXF开箱即用效果很好。 CXF要求你注册其servletFilter作为@Bean在你的应用背景下。 Jersey 有一些原生的 Spring 支持,所以我们在 Spring Boot 中也提供了自动配置支持,并附带一个启动程序。spring-doc.cadn.net.cn

要开始使用Jersey,请包含春季靴-首发球衣作为依赖,然后你需要一个@Bean类型ResourceConfig你注册所有端点,如下示例所示:spring-doc.cadn.net.cn

import org.glassfish.jersey.server.ResourceConfig;

import org.springframework.stereotype.Component;

@Component
public class MyJerseyConfig extends ResourceConfig {

    public MyJerseyConfig() {
        register(MyEndpoint.class);
    }

}
Jersey对扫描可执行档案的支持相当有限。 例如,它无法扫描包中完全可执行的 jar 文件中的端点,或者网络基础/课程运行可执行的战争文件时。 为避免此限制,以下不应使用该方法,端点应通过使用注册如前例所示。

对于更高级的自定义,你也可以注册任意数量的豆子来实现ResourceConfigCustomizer.spring-doc.cadn.net.cn

所有注册端点都应该是@Components带有 HTTP 资源注释(@GET以及其他),如下例所示:spring-doc.cadn.net.cn

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";
    }

}

自从......端点是泉水@Component,其生命周期由Spring管理,你可以使用@Autowired注释以注入依赖关系并使用@Value注释以注入外部配置。 默认情况下,Jerseyservlet被注册并映射到。 你可以通过添加/*@ApplicationPath给你的ResourceConfig.spring-doc.cadn.net.cn

默认情况下,Jersey被设置为@Bean类型ServletRegistrationBean球衣Servlet注册. 默认情况下,servlet是懒惰初始化的,但你可以通过设置来自定义这个行为spring.jersey.servlet.load-on-startup. 你可以通过创建同名的自己豆子来禁用或覆盖那个豆子。 你也可以用Filter代替servlet,方法是设置spring.jersey.type=filter(在这种情况下,@Bean替换或覆盖为jerseyFilter注册). Filter有一个@Order,你可以用 来设置春季.Jersey.过滤器.订单. 使用 Jersey 作为过滤器时,必须有一个 servlet 来处理 Jersey 未拦截的请求。 如果您的应用程序不包含这样的 servlet,您可能想通过设置来启用默认 servlet。server.servlet.register-default-servlettrue. servlet 和Filter注册都可以通过以下方式给定初始化参数春季。Jersey.init.*以指定性质映射。spring-doc.cadn.net.cn

1.3. 嵌入式 Servlet 容器支持

对于 servlet 应用,Spring Boot 支持嵌入式 TomcatJettyUndertow 服务器。 大多数开发者使用相应的“启动程序”来获得一个完整配置的实例。 默认情况下,嵌入式服务器会监听端口上的HTTP请求8080.spring-doc.cadn.net.cn

1.3.1. servlets、Filter与监听器

使用嵌入式 servlet 容器时,你可以注册 servlet、filter,以及所有监听器(例如HttpSessionListener)通过使用 Spring 豆子或扫描 servlet 组件,从服务器规范中提取。spring-doc.cadn.net.cn

注册servlets、Filter和监听器作为春豆

任何servlet,Filter,或servlet。*听者实例 是 Spring Bean 的实例被注册在嵌入的容器中。 如果你想引用 Mine 的某个值,这尤其方便application.properties在配置过程中。spring-doc.cadn.net.cn

默认情况下,如果上下文仅包含一个 Servlet,则映射到 。 对于多个servlet豆,豆名作为路径前缀使用。 筛选器映射到。//*spring-doc.cadn.net.cn

如果基于约定的映射不够灵活,你可以使用ServletRegistrationBean,FilterRegistrationBeanServletListenerRegistrationBean为了完全控制。spring-doc.cadn.net.cn

过滤豆通常可以不按顺序放置。 如果需要特定订单,你应该在Filter@Order或者让它实现命令. 你无法配置 的顺序Filter通过对其豆子方法进行注释,记入@Order. 如果你无法更改Filter需要添加的类别@Order或者实现命令,你必须定义一个FilterRegistrationBean对于Filter并用setOrder(int)方法。 避免配置一个读取请求主体的过滤器Ordered.HIGHEST_PRECEDENCE因为这可能违背你应用的字符编码配置。 如果 servlet 过滤器包裹请求,应配置其顺序小于OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER.spring-doc.cadn.net.cn

查看每个的顺序Filter在您的应用程序中,启用调试级别日志Web Logging组 (logging.level.web=调试). 注册过滤器的详细信息,包括顺序和URL模式,将在启动时被记录。
注册时请小心Filter因为它们在应用生命周期的早期就被初始化。 如果你需要注册Filter如果会与其他豆子相互作用,可以考虑使用DelegatingFilterProxyRegistrationBean相反。

1.3.2. Servlet 上下文初始化

嵌入的servlet容器不会直接执行jakarta.servlet.ServletContainerInitializer界面或Spring的org.springframework.web.WebApplicationInitializer接口。 这是一个有意设计的决策,旨在降低第三方库在战争中运行时可能破坏 Spring Boot 应用的风险。spring-doc.cadn.net.cn

如果你需要在 Spring Boot 应用中执行 servlet 上下文初始化,你应该注册一个实现org.springframework.boot.web.servlet.ServletContextInitializer接口。 单曲onStartup方法提供访问ServletContext如有必要,也可以轻松作为现有WebApplicationInitializer.spring-doc.cadn.net.cn

扫描 Servlets、Filter和听众

使用嵌入式容器时,自动注册类,注释为@WebServlet,@WebFilter@WebListener可以通过以下方式来启用@ServletComponentScan.spring-doc.cadn.net.cn

@ServletComponentScan在独立容器中则无效,因为容器内置的发现机制被使用。

1.3.3. ServletWebServerApplicationContext

在引擎盖底下,Spring Boot采用了另一种应用上下文用于嵌入式 Servlet 容器支持。 这ServletWebServerApplicationContext是一种特殊类型的WebApplicationContext它通过寻找单一的ServletWebServerFactory豆。 通常是TomcatServletWebServerFactory,JettyServletWebServerFactoryUndertowServletWebServerFactory已被自动配置。spring-doc.cadn.net.cn

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

在嵌入式容器设置中,ServletContext作为服务器启动的一部分,启动发生在应用上下文初始化期间。 因为这个豆子在应用上下文无法可靠地用ServletContext. 解决这个问题的一种方法是注射应用上下文作为豆子和访问的依赖ServletContext只有在需要的时候才会这样。 另一种方法是服务器启动后使用回调。 这可以通过ApplicationListener该系统监听ApplicationStartedEvent如下:spring-doc.cadn.net.cn

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();
    }

}

1.3.4. 自定义嵌入式 Servlet 容器

常见的servlet容器设置可以通过Spring进行配置环境性能。 通常,你会定义application.propertiesapplication.yaml文件。spring-doc.cadn.net.cn

常见的服务器设置包括:spring-doc.cadn.net.cn

Spring Boot 尽力暴露常见设置,但这并不总是可行。 对于这些情况,专用命名空间提供服务器特定的自定义(参见服务器.tomcatserver.undertow). 例如,访问日志可以配置嵌入 servlet 容器的特定功能。spring-doc.cadn.net.cn

参见ServerProperties完整列表。
SameSite Cookies

同站Cookie属性可以被网页浏览器用来控制Cookie是否以及如何提交跨站请求。 该属性对于现代网页浏览器尤为重要,因为它们开始更改该属性缺失时使用的默认值。spring-doc.cadn.net.cn

如果你想更改同站你的会话Cookie属性,你可以使用server.servlet.session.cookie.same-site财产。 该特性由自动配置的Tomcat、Jetty和Undertow服务器支持。 它也用于基于 Spring Session servlet 的配置会话仓库豆。spring-doc.cadn.net.cn

例如,如果你希望你的会话Cookie拥有同站属性没有,你可以在你的application.propertiesapplication.yaml文件:spring-doc.cadn.net.cn

性能
server.servlet.session.cookie.same-site=none
Yaml
server:
  servlet:
    session:
      cookie:
        same-site: "none"

如果你想更改同站添加到您的其他Cookie上,属性HttpServletResponse(服务服务响应),你可以用CookieSameSiteSupplier. 这CookieSameSiteSupplier经过a饼干并且可以返回同站值,或.spring-doc.cadn.net.cn

有许多便利工厂和过滤方法可以快速匹配特定cookie。 例如,添加以下豆子会自动应用同站宽松对于所有名称与正则表达式相符的 Cookie我的应用。*.spring-doc.cadn.net.cn

Java
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.*");
    }

}
Kotlin
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.*")
    }

}
字符编码

嵌入式服务容器用于请求和响应处理的字符编码行为可以通过以下方式配置server.servlet.encoding.*配置属性。spring-doc.cadn.net.cn

当请求接受语言头 表示请求的所在地,servlet 容器会自动将其映射到一个字符集。 每个容器的提供商默认为字符集映射,你应该确认它们符合你应用的需求。 如果没有,可以使用server.servlet.encoding.mapping配置属性用于自定义映射,如下示例所示:spring-doc.cadn.net.cn

性能
server.servlet.encoding.mapping.ko=UTF-8
Yaml
server:
  servlet:
    encoding:
      mapping:
        ko: "UTF-8"

在前面的例子中,Ko(韩语)地点已映射为UTF-8. 这等价于<locale-encoding-mapping-list>web.xml传统战争部署的档案。spring-doc.cadn.net.cn

程序化定制

如果你需要程序化配置嵌入式 servlet 容器,可以注册一个实现WebServerFactoryCustomizer接口。WebServerFactoryCustomizer提供访问ConfigurableServletWebServerFactory其中包含多种自定义设置器方法。 以下示例展示了程序化设置端口:spring-doc.cadn.net.cn

Java
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);
    }

}
Kotlin
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,JettyServletWebServerFactoryUndertowServletWebServerFactory是 的专用变体ConfigurableServletWebServerFactory分别为Tomcat、Jetty和Undertow提供了额外的自定义设定方法。 以下示例展示了如何自定义TomcatServletWebServerFactory提供针对Tomcat的专属配置选项:spring-doc.cadn.net.cn

Java
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()));
    }

}
Kotlin
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你自己可以暴露出这种类型的豆子。spring-doc.cadn.net.cn

设置杆提供多种配置选项。 如果你想做一些更特别的事情,游戏还提供了几个安全的“钩子”。 详情请参见源代码文档spring-doc.cadn.net.cn

自动配置的自定义工具仍然会应用到你的自定义工厂,所以要谨慎使用这个选项。

1.3.5. JSP 局限性

当运行使用嵌入式 servlet 容器(并以可执行档案形式打包)的 Spring Boot 应用时,JSP 支持存在一些限制。spring-doc.cadn.net.cn

2. 响应式网页应用

Spring Boot 通过为 Spring Webflux 提供自动配置,简化了响应式网页应用的开发。spring-doc.cadn.net.cn

2.1. “Spring WebFlux 框架”

Spring WebFlux 是 Spring Framework 5.0 中引入的新响应式网页框架。 与 Spring MVC 不同,它不需要 servlet API,完全异步且非阻塞,并通过 Reactor 项目实现了反应流规范。spring-doc.cadn.net.cn

Spring WebFlux 有两种版本:功能型和基于注释型。 基于注释的模型与Spring MVC模型非常接近,如下示例所示:spring-doc.cadn.net.cn

Java
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

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 Mono<User> getUser(@PathVariable Long userId) {
        return this.userRepository.findById(userId);
    }

    @GetMapping("/{userId}/customers")
    public Flux<Customer> getUserCustomers(@PathVariable Long userId) {
        return this.userRepository.findById(userId).flatMapMany(this.customerRepository::findByUser);
    }

    @DeleteMapping("/{userId}")
    public Mono<Void> deleteUser(@PathVariable Long userId) {
        return this.userRepository.deleteById(userId);
    }

}
Kotlin
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
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono

@RestController
@RequestMapping("/users")
class MyRestController(private val userRepository: UserRepository, private val customerRepository: CustomerRepository) {

    @GetMapping("/{userId}")
    fun getUser(@PathVariable userId: Long): Mono<User?> {
        return userRepository.findById(userId)
    }

    @GetMapping("/{userId}/customers")
    fun getUserCustomers(@PathVariable userId: Long): Flux<Customer> {
        return userRepository.findById(userId).flatMapMany { user: User? ->
            customerRepository.findByUser(user)
        }
    }

    @DeleteMapping("/{userId}")
    fun deleteUser(@PathVariable userId: Long): Mono<Void> {
        return userRepository.deleteById(userId)
    }

}

WebFlux 是 Spring 框架的一部分,详细信息可见其参考文档spring-doc.cadn.net.cn

“WebFlux.fn”功能变体将路由配置与实际请求处理分离,如下示例所示:spring-doc.cadn.net.cn

Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;

@Configuration(proxyBeanMethods = false)
public class MyRoutingConfiguration {

    private static final RequestPredicate ACCEPT_JSON = accept(MediaType.APPLICATION_JSON);

    @Bean
    public RouterFunction<ServerResponse> monoRouterFunction(MyUserHandler userHandler) {
        return route()
                .GET("/{user}", ACCEPT_JSON, userHandler::getUser)
                .GET("/{user}/customers", ACCEPT_JSON, userHandler::getUserCustomers)
                .DELETE("/{user}", ACCEPT_JSON, userHandler::deleteUser)
                .build();
    }

}
Kotlin
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.web.reactive.function.server.RequestPredicates.DELETE
import org.springframework.web.reactive.function.server.RequestPredicates.GET
import org.springframework.web.reactive.function.server.RequestPredicates.accept
import org.springframework.web.reactive.function.server.RouterFunction
import org.springframework.web.reactive.function.server.RouterFunctions
import org.springframework.web.reactive.function.server.ServerResponse

@Configuration(proxyBeanMethods = false)
class MyRoutingConfiguration {

    @Bean
    fun monoRouterFunction(userHandler: MyUserHandler): RouterFunction<ServerResponse> {
        return RouterFunctions.route(
            GET("/{user}").and(ACCEPT_JSON), userHandler::getUser).andRoute(
            GET("/{user}/customers").and(ACCEPT_JSON), userHandler::getUserCustomers).andRoute(
            DELETE("/{user}").and(ACCEPT_JSON), userHandler::deleteUser)
    }

    companion object {
        private val ACCEPT_JSON = accept(MediaType.APPLICATION_JSON)
    }

}
Java
import reactor.core.publisher.Mono;

import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;

@Component
public class MyUserHandler {

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

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

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

}
Kotlin
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono

@Component
class MyUserHandler {

    fun getUser(request: ServerRequest?): Mono<ServerResponse> {
        return ServerResponse.ok().build()
    }

    fun getUserCustomers(request: ServerRequest?): Mono<ServerResponse> {
        return ServerResponse.ok().build()
    }

    fun deleteUser(request: ServerRequest?): Mono<ServerResponse> {
        return ServerResponse.ok().build()
    }

}

“WebFlux.fn”是Spring框架的一部分,详细信息可见于其参考文档中。spring-doc.cadn.net.cn

你可以定义多少路由器功能豆子,你喜欢模块化路由器的定义。 如果需要优先顺序,可以订购Beans。

要开始,请添加Spring BootStarters网流模块。spring-doc.cadn.net.cn

两者都加入Spring Boot启动网Spring BootStarters网流你应用中的模块是 Spring Boot 自动配置 Spring MVC,而不是 WebFlux。 之所以选择这种行为,是因为许多 Spring 开发者会添加Spring BootStarters网流使用响应式Web客户端. 你仍然可以通过设置所选应用类型来强制执行你的选择SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE).

2.1.1. Spring WebFlux 自动配置

Spring Boot 为 Spring WebFlux 提供了自动配置,适用于大多数应用程序。spring-doc.cadn.net.cn

自动配置在 Spring 默认设置基础上增加了以下功能:spring-doc.cadn.net.cn

如果你想保留 Spring Boot 的 WebFlux 功能,并且想添加额外的 WebFlux 配置,你可以自己添加@Configuration类型类别WebFluxConfigurer没有 @EnableWebFlux.spring-doc.cadn.net.cn

如果你想完全掌控Spring WebFlux,可以添加自己的@Configuration注释为@EnableWebFlux.spring-doc.cadn.net.cn

2.1.2. Spring WebFlux 转换服务

如果你想自定义转换服务Spring WebFlux 使用,你可以提供WebFluxConfigurer带有addFormatters方法。spring-doc.cadn.net.cn

转换也可以通过以下方式进行定制spring.webflux.format.*配置属性。 未配置时,使用以下默认值:spring-doc.cadn.net.cn

属性 DateTimeFormatter

春季.webflux.format.datespring-doc.cadn.net.cn

ofLocalizedDate(FormatStyle.SHORT)spring-doc.cadn.net.cn

Spring.webflux.format.timespring-doc.cadn.net.cn

ofLocalizedTime(FormatStyle.SHORT)spring-doc.cadn.net.cn

spring.webflux.format.date-timespring-doc.cadn.net.cn

ofLocalizedDateTime(FormatStyle.SHORT)spring-doc.cadn.net.cn

2.1.3. 带有 HttpMessageReaders 和 HttpMessageWriters 的 HTTP 编解码器

Spring WebFlux 使用HttpMessageReaderHttpMessageWriter用于转换HTTP请求和响应的接口。 它们配置为CodecConfigurer通过查看类路径中的库来制定合理的默认值。spring-doc.cadn.net.cn

Spring Boot 为编解码器提供了专用的配置属性,spring.codec.*. 它还通过以下方式进行进一步的自定义CodecCustomizer实例。 例如Spring。Jackson。*配置键被应用到Jackson编解码器上。spring-doc.cadn.net.cn

如果你需要添加或自定义编解码器,可以创建一个自定义的CodecCustomizer组件,如下例所示:spring-doc.cadn.net.cn

Java
import org.springframework.boot.web.codec.CodecCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.codec.ServerSentEventHttpMessageReader;

@Configuration(proxyBeanMethods = false)
public class MyCodecsConfiguration {

    @Bean
    public CodecCustomizer myCodecCustomizer() {
        return (configurer) -> {
            configurer.registerDefaults(false);
            configurer.customCodecs().register(new ServerSentEventHttpMessageReader());
            // ...
        };
    }

}
Kotlin
import org.springframework.boot.web.codec.CodecCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.http.codec.CodecConfigurer
import org.springframework.http.codec.ServerSentEventHttpMessageReader

class MyCodecsConfiguration {

    @Bean
    fun myCodecCustomizer(): CodecCustomizer {
        return CodecCustomizer { configurer: CodecConfigurer ->
            configurer.registerDefaults(false)
            configurer.customCodecs().register(ServerSentEventHttpMessageReader())
        }
    }

}

2.1.4. 静态内容

默认情况下,Spring Boot 从名为/静态的(或/公共/资源/元补强/资源)在 classpath 中。 它使用ResourceWebHandler从Spring WebFlux下载,这样你可以通过添加自己的行为来修改该行为WebFluxConfigurer并且覆盖addResourceHandlers方法。spring-doc.cadn.net.cn

默认情况下,资源映射在 ,但你可以通过设置/**春网通量静态路径模式财产。 例如,将所有资源重新定位到/资源/**可以实现如下:spring-doc.cadn.net.cn

性能
spring.webflux.static-path-pattern=/resources/**
Yaml
spring:
  webflux:
    static-path-pattern: "/resources/**"

你也可以通过以下方式自定义静态资源位置spring.web.resources.static-locations. 这样做会将默认值替换为目录位置列表。 如果这样做,默认的欢迎页面检测会切换到你的自定义位置。 所以,如果存在index.html在你任何一个启动地点,它都是应用的主页。spring-doc.cadn.net.cn

除了前面提到的“标准”静态资源位置外,Webjars 内容还有一个特殊情况。 默认情况下,任何路径在 的资源/webjars/ **如果 jar 文件以 Webjars 格式打包,则 是从中提供。 路径可以通过以下方式进行自定义春.webflux.webjars-path-pattern财产。spring-doc.cadn.net.cn

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

2.1.5. 欢迎页面

Spring Boot 支持静态和模板欢迎页。 它首先寻找一个index.html文件位于配置好的静态内容位置。 如果找不到,则会寻找指数模板。 如果找到其中任何一个,它会自动作为申请的欢迎页面使用。spring-doc.cadn.net.cn

2.1.6. 模板引擎

除了 REST Web 服务,你还可以使用 Spring WebFlux 来提供动态 HTML 内容。 Spring WebFlux 支持多种模板技术,包括 Thymeleaf、FreeMark 和 Mustache。spring-doc.cadn.net.cn

Spring Boot 支持以下模板引擎的自动配置:spring-doc.cadn.net.cn

当你使用这些模板引擎中的默认配置时,模板会自动从以下获取src/main/resources/模板.spring-doc.cadn.net.cn

2.1.7. 错误处理

Spring靴提供了WebExceptionHandler这样能合理处理所有错误。 它在处理顺序中的位置紧接WebFlux提供的处理程序,这些处理程序被视为最后。 对于机器客户端,它会生成包含错误信息、HTTP 状态和异常消息的 JSON 响应。 对于浏览器客户端,有一个“白标”错误处理程序,可以将相同数据渲染成HTML格式。 你也可以提供自己的HTML模板来显示错误(见下一节)。spring-doc.cadn.net.cn

在直接自定义 Spring Boot 的错误处理之前,你可以利用 Spring WebFlux 中的 RFC 7807 问题细节支持。 Spring WebFlux 可以生成自定义错误信息,包括application/problem+json媒体类型,比如:spring-doc.cadn.net.cn

{
  "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.webflux.problemdetails.enabledtrue.spring-doc.cadn.net.cn

定制此功能的第一步通常是使用现有机制,但替换或增强错误内容。 为此,你可以添加一颗 类型的豆子错误属性.spring-doc.cadn.net.cn

要改变错误处理行为,你可以实现ErrorWebExceptionHandler并注册该类型的豆子定义。 因为ErrorWebExceptionHandler这个等级相当低,Spring靴也提供了方便的抽象错误WebExceptionHandler以便你能以WebFlux函数式的方式处理错误,如下示例所示:spring-doc.cadn.net.cn

Java
import reactor.core.publisher.Mono;

import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.function.server.ServerResponse.BodyBuilder;

@Component
public class MyErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {

    public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes, WebProperties webProperties,
            ApplicationContext applicationContext, ServerCodecConfigurer serverCodecConfigurer) {
        super(errorAttributes, webProperties.getResources(), applicationContext);
        setMessageReaders(serverCodecConfigurer.getReaders());
        setMessageWriters(serverCodecConfigurer.getWriters());
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(this::acceptsXml, this::handleErrorAsXml);
    }

    private boolean acceptsXml(ServerRequest request) {
        return request.headers().accept().contains(MediaType.APPLICATION_XML);
    }

    public Mono<ServerResponse> handleErrorAsXml(ServerRequest request) {
        BodyBuilder builder = ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR);
        // ... additional builder calls
        return builder.build();
    }

}
Kotlin
import org.springframework.boot.autoconfigure.web.WebProperties
import org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler
import org.springframework.boot.web.reactive.error.ErrorAttributes
import org.springframework.context.ApplicationContext
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.codec.ServerCodecConfigurer
import org.springframework.stereotype.Component
import org.springframework.web.reactive.function.server.RouterFunction
import org.springframework.web.reactive.function.server.RouterFunctions
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import reactor.core.publisher.Mono

@Component
class MyErrorWebExceptionHandler(
        errorAttributes: ErrorAttributes, webProperties: WebProperties,
        applicationContext: ApplicationContext, serverCodecConfigurer: ServerCodecConfigurer
) : AbstractErrorWebExceptionHandler(errorAttributes, webProperties.resources, applicationContext) {

    init {
        setMessageReaders(serverCodecConfigurer.readers)
        setMessageWriters(serverCodecConfigurer.writers)
    }

    override fun getRoutingFunction(errorAttributes: ErrorAttributes): RouterFunction<ServerResponse> {
        return RouterFunctions.route(this::acceptsXml, this::handleErrorAsXml)
    }

    private fun acceptsXml(request: ServerRequest): Boolean {
        return request.headers().accept().contains(MediaType.APPLICATION_XML)
    }

    fun handleErrorAsXml(request: ServerRequest): Mono<ServerResponse> {
        val builder = ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
        // ... additional builder calls
        return builder.build()
    }

}

为了更完整的情况,你也可以选择子职业默认错误网页例外处理器直接覆盖特定方法。spring-doc.cadn.net.cn

在某些情况下,控制器或处理程序层处理的错误不会被度量基础设施记录。 应用程序可以通过将处理异常设置为请求属性来确保这些异常与请求指标一起记录:spring-doc.cadn.net.cn

Java
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.reactive.result.view.Rendering;
import org.springframework.web.server.ServerWebExchange;

@Controller
public class MyExceptionHandlingController {

    @GetMapping("/profile")
    public Rendering userProfile() {
        // ...
        throw new IllegalStateException();
    }

    @ExceptionHandler(IllegalStateException.class)
    public Rendering handleIllegalState(ServerWebExchange exchange, IllegalStateException exc) {
        exchange.getAttributes().putIfAbsent(ErrorAttributes.ERROR_ATTRIBUTE, exc);
        return Rendering.view("errorView").modelAttribute("message", exc.getMessage()).build();
    }

}
Kotlin
import org.springframework.boot.web.reactive.error.ErrorAttributes
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.reactive.result.view.Rendering
import org.springframework.web.server.ServerWebExchange

@Controller
class MyExceptionHandlingController {

    @GetMapping("/profile")
    fun userProfile(): Rendering {
        // ...
        throw IllegalStateException()
    }

    @ExceptionHandler(IllegalStateException::class)
    fun handleIllegalState(exchange: ServerWebExchange, exc: IllegalStateException): Rendering {
        exchange.attributes.putIfAbsent(ErrorAttributes.ERROR_ATTRIBUTE, exc)
        return Rendering.view("errorView").modelAttribute("message", exc.message ?: "").build()
    }

}
自定义错误页面

如果你想为某个状态码显示自定义的 HTML 错误页面,可以添加由错误/*,例如通过向/错误目录。 错误页面可以是静态 HTML(即添加到任何静态资源目录下),也可以用模板构建。 文件名称应为准确的状态码、状态码系列掩码,或错误如果其他条件匹配,默认情况下也没问题。 注意,通往默认误差视图的路径为错误/错误而 Spring MVC 的默认错误视图是错误.spring-doc.cadn.net.cn

例如,映射404对于静态HTML文件,你的目录结构如下:spring-doc.cadn.net.cn

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

映射所有5xx使用Mustache模板时,你的目录结构如下:spring-doc.cadn.net.cn

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

2.1.8. 网页过滤器

Spring WebFlux 提供了WebFilter可以实现用于过滤HTTP请求-响应交换的接口。WebFilter应用上下文中发现的豆子会自动用于过滤每次交换。spring-doc.cadn.net.cn

如果Filter的顺序很重要,他们可以实现命令或者注释为@Order. Spring Boot 自动配置可能会帮你配置网页过滤器。 当它这样做时,将使用下表所示的顺序:spring-doc.cadn.net.cn

网页过滤 次序

ServerHttpObservationFilter(微米可探测性)spring-doc.cadn.net.cn

Ordered.HIGHEST_PRECEDENCE + 1spring-doc.cadn.net.cn

WebFilterChainProxy(春季安全)spring-doc.cadn.net.cn

-100spring-doc.cadn.net.cn

HttpExchangesWebFilterspring-doc.cadn.net.cn

Ordered.LOWEST_PRECEDENCE - 10spring-doc.cadn.net.cn

2.2. 嵌入式响应式服务器支持

Spring Boot 支持以下嵌入式响应式 Web 服务器:Reactor Netty、Tomcat、Jetty 和 Undertow。 大多数开发者使用相应的“启动程序”来获得一个完整配置的实例。 默认情况下,嵌入式服务器会监听8080端口上的HTTP请求。spring-doc.cadn.net.cn

2.2.1. 自定义响应式服务器

常见的响应式网页服务器设置可以通过 Spring 进行配置环境性能。 通常,你会定义application.propertiesapplication.yaml文件。spring-doc.cadn.net.cn

常见的服务器设置包括:spring-doc.cadn.net.cn

Spring Boot 尽力暴露常见设置,但这并不总是可行。 在这种情况下,专用命名空间如server.netty.*提供服务器专属定制。spring-doc.cadn.net.cn

参见ServerProperties完整列表。
程序化定制

如果你需要程序化配置你的响应式网页服务器,你可以注册一个实现WebServerFactoryCustomizer接口。WebServerFactoryCustomizer提供访问ConfigurableReactiveWebServerFactory其中包含多种自定义设置器方法。 以下示例展示了程序化设置端口:spring-doc.cadn.net.cn

Java
import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class MyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<ConfigurableReactiveWebServerFactory> {

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

}
Kotlin
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory
import org.springframework.stereotype.Component

@Component
class MyWebServerFactoryCustomizer : WebServerFactoryCustomizer<ConfigurableReactiveWebServerFactory> {

    override fun customize(server: ConfigurableReactiveWebServerFactory) {
        server.setPort(9000)
    }

}

JettyReactiveWebServerFactory,NettyReactiveWebServerFactory,TomcatReactiveWebServerFactoryUndertowServletWebServerFactory是 的专用变体ConfigurableReactiveWebServerFactory分别为Jetty、Reactor Netty、Tomcat和Undertow提供了额外的自定义设定方法。 以下示例展示了如何自定义NettyReactiveWebServerFactory提供针对Reactor Netty的专属配置选项:spring-doc.cadn.net.cn

Java
import java.time.Duration;

import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class MyNettyWebServerFactoryCustomizer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {

    @Override
    public void customize(NettyReactiveWebServerFactory factory) {
        factory.addServerCustomizers((server) -> server.idleTimeout(Duration.ofSeconds(20)));
    }

}
Kotlin
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer
import org.springframework.stereotype.Component
import java.time.Duration

@Component
class MyNettyWebServerFactoryCustomizer : WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {

    override fun customize(factory: NettyReactiveWebServerFactory) {
        factory.addServerCustomizers({ server -> server.idleTimeout(Duration.ofSeconds(20)) })
    }

}
直接自定义可配置的ReactiveWebServerFactory

对于需要扩展的高级用例ReactiveWebServerFactory你自己可以暴露出这种类型的豆子。spring-doc.cadn.net.cn

设置杆提供多种配置选项。 如果你想做一些更特别的事情,游戏还提供了几个安全的“钩子”。 详情请参见源代码文档spring-doc.cadn.net.cn

自动配置的自定义工具仍然会应用到你的自定义工厂,所以要谨慎使用这个选项。

2.3. 响应式服务器资源配置

在自动配置 Reactor Netty 或 Jetty 服务器时,Spring Boot 会创建特定的 BEANS,为服务器实例提供 HTTP 资源:反应堆资源工厂Jetty资源工厂.spring-doc.cadn.net.cn

默认情况下,这些资源也会与反应堆Netty和Jetty客户端共享,以实现最佳性能,满足以下条件:spring-doc.cadn.net.cn

开发者可以通过提供自定义配置来覆盖Jetty和Reactor Netty的资源配置反应堆资源工厂Jetty资源工厂BEAN - 这将同时应用于客户端和服务器。spring-doc.cadn.net.cn

你可以在 WebClient 运行时部分了解更多关于客户端资源配置的信息。spring-doc.cadn.net.cn

3. 优雅关机

优雅关机支持所有四个嵌入式Web服务器(Jetty、Reactor Netty、Tomcat和Undertow)以及响应式和基于servlet的Web应用程序。 它作为关闭应用上下文的一部分出现,并在停止的最早阶段执行SmartLifecycle豆。 该停止处理使用超时,提供宽限期,允许现有请求完成,但不允许新请求。 禁止新请求的具体方式取决于所使用的网络服务器。 Jetty、Reactor Netty 和 Tomcat 将停止在网络层接受请求。 Undertow会接受请求,但会立即回复为“服务不可用”(503)“。spring-doc.cadn.net.cn

使用 Tomcat 实现优雅关机需要 Tomcat 9.0.33 或更高版本。

要启用优雅关机,请配置server.shutdown性质,如下例所示:spring-doc.cadn.net.cn

性能
server.shutdown=graceful
Yaml
server:
  shutdown: "graceful"

要配置超时时间,请设置spring.lifecycle.timeout-per-shutdown-phase性质,如下例所示:spring-doc.cadn.net.cn

性能
spring.lifecycle.timeout-per-shutdown-phase=20s
Yaml
spring:
  lifecycle:
    timeout-per-shutdown-phase: "20s"
如果你的IDE没有发送正确的信号,使用优雅关机可能无法正常工作SIGTERM信号。 详情请参见你的IDE文档。

4. 春季安全

如果 Spring Security 在类路径上,那么 Web 应用默认是安全的。 Spring Boot 依赖于 Spring Security 的内容协商策略来决定是否使用httpBasic表格登录. 为了为网页应用添加方法级安全性,你也可以添加@EnableGlobalMethodSecurity用你想要的设置。 更多信息可见《春季安全参考指南》。spring-doc.cadn.net.cn

默认用户详情服务只有一个用户。 用户名是用户密码是随机的,应用程序启动时以WARN级别打印,如下示例所示:spring-doc.cadn.net.cn

Using generated security password: 78fa095d-3f4c-48b1-ad50-e24c31d5cf35

This generated password is for development use only. Your security configuration must be updated before running your application in production.
如果你微调日志配置,确保org.springframework.boot.autoconfigure.security类别设置为对数警告——级别的信息。 否则,默认密码不会被打印出来。

您可以通过提供以下信息来更改用户名和密码spring.security.user.namespring.security.user.password.spring-doc.cadn.net.cn

你默认在网页应用中获得的基本功能包括:spring-doc.cadn.net.cn

  • 一个用户详情服务(或ReactiveUserDetailsService对于WebFlux应用来说),具有内存存储的豆子和一个生成密码的用户(参见SecurityProperties.User对于用户的属性)。spring-doc.cadn.net.cn

  • 基于表单的登录或HTTP Basic安全(取决于接受请求中的头部)用于整个应用程序(包括执行器端点,如果执行器在类路径上)。spring-doc.cadn.net.cn

  • 一个默认认证事件发布者用于发布认证事件。spring-doc.cadn.net.cn

你可以提供不同的选择认证事件出版者通过加一颗豆子。spring-doc.cadn.net.cn

4.1. MVC安全

默认安全配置实现于安全自动配置UserDetailsServiceAutoConfiguration.安全自动配置进口SpringBootWebSecurityConfiguration用于网络安全和UserDetailsServiceAutoConfiguration配置认证,这在非网页应用中同样重要。 要完全关闭默认的 Web 应用安全配置,或合并多个 Spring Security 组件,如 OAuth2 客户端和资源服务器,请添加 的 bean安全滤网链(这样做并不会禁用用户详情服务配置或执行器的安全性)。spring-doc.cadn.net.cn

还要关闭用户详情服务配置时,你可以添加一个类型的豆子用户详情服务,认证提供者认证管理器.spring-doc.cadn.net.cn

访问规则可以通过添加自定义规则来覆盖安全滤网链豆。 Spring Boot 提供了方便的方法,可用于覆盖执行器端点和静态资源的访问规则。端点请求可以用来创建请求匹配器基于管理端点.web.base-path财产。路径请求可以用来创建请求匹配器针对常用地点的资源。spring-doc.cadn.net.cn

4.2. WebFlux 安全

类似于 Spring MVC 应用程序,您可以通过添加Spring Boot启动Dependency。 默认安全配置实现于ReactiveSecurityAutoConfigurationUserDetailsServiceAutoConfiguration.ReactiveSecurityAutoConfiguration进口WebFluxSecurityConfiguration用于网络安全和UserDetailsServiceAutoConfiguration配置认证,这在非网页应用中同样重要。 要完全关闭默认的网页应用安全配置,可以添加一个 类型的豆子WebFilterChainProxy(这样做并不会禁用用户详情服务配置或执行器的安全性)。spring-doc.cadn.net.cn

还要关闭用户详情服务配置时,你可以添加一个类型的豆子ReactiveUserDetailsServiceReactiveAuthenticationManager.spring-doc.cadn.net.cn

访问规则以及多个 Spring Security 组件(如 OAuth 2 客户端和资源服务器)的使用,可以通过添加自定义配置来配置安全网滤网链豆。 Spring Boot 提供了方便的方法,可用于覆盖执行器端点和静态资源的访问规则。端点请求可以用来创建ServerWebExchangeMatcher基于管理端点.web.base-path财产。spring-doc.cadn.net.cn

路径请求可以用来创建ServerWebExchangeMatcher针对常用地点的资源。spring-doc.cadn.net.cn

例如,你可以通过添加以下内容来自定义你的安全配置:spring-doc.cadn.net.cn

Java
import org.springframework.boot.autoconfigure.security.reactive.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration(proxyBeanMethods = false)
public class MyWebFluxSecurityConfiguration {

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http.authorizeExchange((exchange) -> {
            exchange.matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll();
            exchange.pathMatchers("/foo", "/bar").authenticated();
        });
        http.formLogin(withDefaults());
        return http.build();
    }

}
Kotlin
import org.springframework.boot.autoconfigure.security.reactive.PathRequest
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.web.server.SecurityWebFilterChain

@Configuration(proxyBeanMethods = false)
class MyWebFluxSecurityConfiguration {

    @Bean
    fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
        http.authorizeExchange { spec ->
            spec.matchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
            spec.pathMatchers("/foo", "/bar").authenticated()
        }
        http.formLogin()
        return http.build()
    }

}

4.3. OAuth2

OAuth2 是一个广泛使用的授权框架,由 Spring 支持。spring-doc.cadn.net.cn

4.3.1. 客户端

如果你有Spring-security-oAuth2-client在你的类路径上,你可以利用一些自动配置来设置 OAuth2/Open ID Connect 客户端。 该配置利用了以下属性OAuth2ClientProperties. 这些特性同样适用于servlet和响应式应用。spring-doc.cadn.net.cn

你可以在spring.security.oauth2.client前缀,如下示例所示:spring-doc.cadn.net.cn

性能
spring.security.oauth2.client.registration.my-login-client.client-id=abcd
spring.security.oauth2.client.registration.my-login-client.client-secret=password
spring.security.oauth2.client.registration.my-login-client.client-name=Client for OpenID Connect
spring.security.oauth2.client.registration.my-login-client.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-login-client.scope=openid,profile,email,phone,address
spring.security.oauth2.client.registration.my-login-client.redirect-uri={baseUrl}/login/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.my-login-client.client-authentication-method=client_secret_basic
spring.security.oauth2.client.registration.my-login-client.authorization-grant-type=authorization_code

spring.security.oauth2.client.registration.my-client-1.client-id=abcd
spring.security.oauth2.client.registration.my-client-1.client-secret=password
spring.security.oauth2.client.registration.my-client-1.client-name=Client for user scope
spring.security.oauth2.client.registration.my-client-1.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-1.scope=user
spring.security.oauth2.client.registration.my-client-1.redirect-uri={baseUrl}/authorized/user
spring.security.oauth2.client.registration.my-client-1.client-authentication-method=client_secret_basic
spring.security.oauth2.client.registration.my-client-1.authorization-grant-type=authorization_code

spring.security.oauth2.client.registration.my-client-2.client-id=abcd
spring.security.oauth2.client.registration.my-client-2.client-secret=password
spring.security.oauth2.client.registration.my-client-2.client-name=Client for email scope
spring.security.oauth2.client.registration.my-client-2.provider=my-oauth-provider
spring.security.oauth2.client.registration.my-client-2.scope=email
spring.security.oauth2.client.registration.my-client-2.redirect-uri={baseUrl}/authorized/email
spring.security.oauth2.client.registration.my-client-2.client-authentication-method=client_secret_basic
spring.security.oauth2.client.registration.my-client-2.authorization-grant-type=authorization_code

spring.security.oauth2.client.provider.my-oauth-provider.authorization-uri=https://my-auth-server.com/oauth2/authorize
spring.security.oauth2.client.provider.my-oauth-provider.token-uri=https://my-auth-server.com/oauth2/token
spring.security.oauth2.client.provider.my-oauth-provider.user-info-uri=https://my-auth-server.com/userinfo
spring.security.oauth2.client.provider.my-oauth-provider.user-info-authentication-method=header
spring.security.oauth2.client.provider.my-oauth-provider.jwk-set-uri=https://my-auth-server.com/oauth2/jwks
spring.security.oauth2.client.provider.my-oauth-provider.user-name-attribute=name
Yaml
spring:
  security:
    oauth2:
      client:
        registration:
          my-login-client:
            client-id: "abcd"
            client-secret: "password"
            client-name: "Client for OpenID Connect"
            provider: "my-oauth-provider"
            scope: "openid,profile,email,phone,address"
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            client-authentication-method: "client_secret_basic"
            authorization-grant-type: "authorization_code"

          my-client-1:
            client-id: "abcd"
            client-secret: "password"
            client-name: "Client for user scope"
            provider: "my-oauth-provider"
            scope: "user"
            redirect-uri: "{baseUrl}/authorized/user"
            client-authentication-method: "client_secret_basic"
            authorization-grant-type: "authorization_code"

          my-client-2:
            client-id: "abcd"
            client-secret: "password"
            client-name: "Client for email scope"
            provider: "my-oauth-provider"
            scope: "email"
            redirect-uri: "{baseUrl}/authorized/email"
            client-authentication-method: "client_secret_basic"
            authorization-grant-type: "authorization_code"

        provider:
          my-oauth-provider:
            authorization-uri: "https://my-auth-server.com/oauth2/authorize"
            token-uri: "https://my-auth-server.com/oauth2/token"
            user-info-uri: "https://my-auth-server.com/userinfo"
            user-info-authentication-method: "header"
            jwk-set-uri: "https://my-auth-server.com/oauth2/jwks"
            user-name-attribute: "name"

对于支持 OpenID Connect 发现的 OpenID Connect 提供商,配置可以进一步简化。 提供者需要配置为发行者-URI即其断言为发行者标识符的URI。 例如,如果发行者-URI只要 是“https://example.com”,则会向“https://example.com/.well-known/openid-configuration”发送“OpenID Provider 配置请求”。 预计结果将是“OpenID Provider 配置响应”。 以下示例展示了如何配置OpenID Connect提供商的发行者-URI:spring-doc.cadn.net.cn

性能
spring.security.oauth2.client.provider.oidc-provider.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/
Yaml
spring:
  security:
    oauth2:
      client:
        provider:
          oidc-provider:
            issuer-uri: "https://dev-123456.oktapreview.com/oauth2/default/"

默认情况下,Spring Security的OAuth2LoginAuthenticationFilter只处理匹配的URL。/login/oauth2/code/*. 如果你想自定义重定向-uri要使用不同的图案,你需要提供配置来处理该自定义图案。 例如,对于servlet应用,你可以添加自己的安全滤网链类似于以下内容:spring-doc.cadn.net.cn

Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
public class MyOAuthClientConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((requests) -> requests
                .anyRequest().authenticated()
            )
            .oauth2Login((login) -> login
                .redirectionEndpoint((endpoint) -> endpoint
                    .baseUri("/login/oauth2/callback/*")
                )
            );
        return http.build();
    }

}
Kotlin
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.invoke
import org.springframework.security.web.SecurityFilterChain

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
open class MyOAuthClientConfiguration {

    @Bean
    open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            authorizeHttpRequests {
                authorize(anyRequest, authenticated)
            }
            oauth2Login {
                redirectionEndpoint {
                    baseUri = "/login/oauth2/callback/*"
                }
            }
        }
        return http.build()
    }

}
Spring Boot 会自动配置InMemoryOAuth2AuthorizedClientServiceSpring Security 用于管理客户端注册。 这InMemoryOAuth2AuthorizedClientService功能有限,我们建议仅在开发环境中使用它。 对于生产环境,考虑使用JdbcOAuth2AuthorizedClientService或者创建你自己的实现OAuth2AuthorizedClientService.
OAuth2 通用服务商客户注册

对于常见的OAuth2和OpenID提供商,包括Google、Github、Facebook和Okta,我们提供了一组提供者默认设置(谷歌,GitHub,脸书分别是 。spring-doc.cadn.net.cn

如果你不需要自定义这些服务商,你可以设置提供商归入你需要推断默认值的那个。 此外,如果客户端注册的密钥与默认支持的提供者匹配,Spring Boot 也会推断出这一点。spring-doc.cadn.net.cn

换句话说,以下示例中的两种配置使用了 Google 提供者:spring-doc.cadn.net.cn

性能
spring.security.oauth2.client.registration.my-client.client-id=abcd
spring.security.oauth2.client.registration.my-client.client-secret=password
spring.security.oauth2.client.registration.my-client.provider=google
spring.security.oauth2.client.registration.google.client-id=abcd
spring.security.oauth2.client.registration.google.client-secret=password
Yaml
spring:
  security:
    oauth2:
      client:
        registration:
          my-client:
            client-id: "abcd"
            client-secret: "password"
            provider: "google"
          google:
            client-id: "abcd"
            client-secret: "password"

4.3.2. 资源服务器

如果你有Spring-security-oAuth2-Resource-Server在你的类路径上,Spring Boot 可以搭建一个 OAuth2 资源服务器。 对于JWT配置,需要指定JWK集URI或OIDC发行URI,如下示例所示:spring-doc.cadn.net.cn

性能
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://example.com/oauth2/default/v1/keys
Yaml
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: "https://example.com/oauth2/default/v1/keys"
性能
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/
Yaml
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: "https://dev-123456.oktapreview.com/oauth2/default/"
如果授权服务器不支持 JWK 集合 URI,你可以用用于验证 JWT 签名的公钥配置资源服务器。 这可以通过spring.security.oauth2.resourceserver.jwt.public-key-location属性,其中值需要指向包含 PEM 编码的 x509 格式公钥的文件。

spring.security.oauth2.resourceserver.jwt.audiences属性可用于指定JWT中AUD索赔的期望值。 例如,要求JWT包含一个AUD权利要求,其价值为我的观众:spring-doc.cadn.net.cn

性能
spring.security.oauth2.resourceserver.jwt.audiences[0]=my-audience
Yaml
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          audiences:
            - "my-audience"

这些特性同样适用于servlet和响应式应用。 或者,你可以自己定义Jwt解码器BEAN 用于 servlet 应用或ReactiveJwt解码器用于被动应用。spring-doc.cadn.net.cn

在使用不透明Tokens代替JWT的情况下,你可以配置以下属性,通过内省验证Tokens:spring-doc.cadn.net.cn

性能
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=https://example.com/check-token
spring.security.oauth2.resourceserver.opaquetoken.client-id=my-client-id
spring.security.oauth2.resourceserver.opaquetoken.client-secret=my-client-secret
Yaml
spring:
  security:
    oauth2:
      resourceserver:
        opaquetoken:
          introspection-uri: "https://example.com/check-token"
          client-id: "my-client-id"
          client-secret: "my-client-secret"

同样,这些性质同样适用于servlet和响应式应用。 或者,你可以自己定义OpaqueTokenIntrospectorBEAN 用于 servlet 应用或ReactiveOpaqueTokenIntrospector用于被动应用。spring-doc.cadn.net.cn

4.3.3. 授权服务器

你可以使用 Spring 授权服务器项目实现 OAuth 2.0 授权服务器。spring-doc.cadn.net.cn

4.4. SAML 2.0

4.4.1. 依赖方

如果你有Spring-security-saml2-service-provider在你的类路径上,你可以利用一些自动配置来设置SAML 2.0依赖方。 该配置利用了以下属性Saml2RelieingPartyProperties(Saml2ReliingPartyProperties).spring-doc.cadn.net.cn

依赖方注册代表身份提供者(IDP)与服务提供商SP之间的配对配置。 你可以根据Spring.security.SAML2.依赖方前缀,如下示例所示:spring-doc.cadn.net.cn

性能
spring.security.saml2.relyingparty.registration.my-relying-party1.signing.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party1.signing.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party1.decryption.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party1.decryption.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party1.singlelogout.url=https://myapp/logout/saml2/slo
spring.security.saml2.relyingparty.registration.my-relying-party1.singlelogout.response-url=https://remoteidp2.slo.url
spring.security.saml2.relyingparty.registration.my-relying-party1.singlelogout.binding=POST
spring.security.saml2.relyingparty.registration.my-relying-party1.assertingparty.verification.credentials[0].certificate-location=path-to-verification-cert
spring.security.saml2.relyingparty.registration.my-relying-party1.assertingparty.entity-id=remote-idp-entity-id1
spring.security.saml2.relyingparty.registration.my-relying-party1.assertingparty.sso-url=https://remoteidp1.sso.url

spring.security.saml2.relyingparty.registration.my-relying-party2.signing.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party2.signing.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party2.decryption.credentials[0].private-key-location=path-to-private-key
spring.security.saml2.relyingparty.registration.my-relying-party2.decryption.credentials[0].certificate-location=path-to-certificate
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.verification.credentials[0].certificate-location=path-to-other-verification-cert
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.entity-id=remote-idp-entity-id2
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.sso-url=https://remoteidp2.sso.url
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.singlelogout.url=https://remoteidp2.slo.url
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.singlelogout.response-url=https://myapp/logout/saml2/slo
spring.security.saml2.relyingparty.registration.my-relying-party2.assertingparty.singlelogout.binding=POST
Yaml
spring:
  security:
    saml2:
      relyingparty:
        registration:
          my-relying-party1:
            signing:
              credentials:
              - private-key-location: "path-to-private-key"
                certificate-location: "path-to-certificate"
            decryption:
              credentials:
              - private-key-location: "path-to-private-key"
                certificate-location: "path-to-certificate"
            singlelogout:
               url: "https://myapp/logout/saml2/slo"
               response-url: "https://remoteidp2.slo.url"
               binding: "POST"
            assertingparty:
              verification:
                credentials:
                - certificate-location: "path-to-verification-cert"
              entity-id: "remote-idp-entity-id1"
              sso-url: "https://remoteidp1.sso.url"

          my-relying-party2:
            signing:
              credentials:
              - private-key-location: "path-to-private-key"
                certificate-location: "path-to-certificate"
            decryption:
              credentials:
              - private-key-location: "path-to-private-key"
                certificate-location: "path-to-certificate"
            assertingparty:
              verification:
                credentials:
                - certificate-location: "path-to-other-verification-cert"
              entity-id: "remote-idp-entity-id2"
              sso-url: "https://remoteidp2.sso.url"
              singlelogout:
                url: "https://remoteidp2.slo.url"
                response-url: "https://myapp/logout/saml2/slo"
                binding: "POST"

对于SAML2的注销,默认情况下,Spring Security的Saml2LogoutRequestFilterSaml2LogoutResponseFilter仅处理匹配的 URL/登出/SAML2/SLO. 如果你想自定义网址AP发起的登出请求会被发送到或响应网址AP会发送登出响应,如果要使用不同的模式,你需要配置来处理该自定义模式。 例如,对于servlet应用,你可以添加自己的安全滤网链类似于以下内容:spring-doc.cadn.net.cn

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration(proxyBeanMethods = false)
public class MySamlRelyingPartyConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests().anyRequest().authenticated();
        http.saml2Login();
        http.saml2Logout((saml2) -> saml2.logoutRequest((request) -> request.logoutUrl("/SLOService.saml2"))
            .logoutResponse((response) -> response.logoutUrl("/SLOService.saml2")));
        return http.build();
    }

}

5. Spring Session

Spring Boot 为各种数据存储提供了 Spring 会话自动配置。 在构建 servlet 网络应用时,可以自动配置以下存储:spring-doc.cadn.net.cn

servlet 自动配置取代了@Enable*HttpSession.spring-doc.cadn.net.cn

如果类路径上存在单个 Spring Session 模块,Spring Boot 会自动使用该存储实现。 如果你有多个实现,Spring Boot 会按照以下顺序选择特定实现:spring-doc.cadn.net.cn

  1. Redisspring-doc.cadn.net.cn

  2. JDBCspring-doc.cadn.net.cn

  3. Hazelcastspring-doc.cadn.net.cn

  4. MongoDBspring-doc.cadn.net.cn

  5. 如果 Redis、JDBC、Hazelcast 和 MongoDB 都不可用,我们不会配置会话仓库.spring-doc.cadn.net.cn

在构建响应式网页应用时,可以自动配置以下存储:spring-doc.cadn.net.cn

响应式自动配置取代了使用@Enable*WebSession.spring-doc.cadn.net.cn

类似于servlet配置,如果你有多个实现,Spring Boot会按照以下顺序选择特定实现:spring-doc.cadn.net.cn

  1. Redisspring-doc.cadn.net.cn

  2. MongoDBspring-doc.cadn.net.cn

  3. 如果 Redis 和 MongoDB 都不可用,我们就不会配置ReactiveSessionRepository.spring-doc.cadn.net.cn

每个商店都有特定的额外设置。 例如,可以自定义JDBC存储表的名称,如下示例所示:spring-doc.cadn.net.cn

性能
spring.session.jdbc.table-name=SESSIONS
Yaml
spring:
  session:
    jdbc:
      table-name: "SESSIONS"

关于设置会话的超时,你可以使用春季.会话.暂停财产。 如果servlet网页应用未设置该属性,自动配置会退回到server.servlet.session.timeout.spring-doc.cadn.net.cn

你可以用以下方式控制 Spring Session 的配置@Enable*HttpSession(servlet)或@Enable*WebSession(反应性)。 这会导致自动配置功能退后。 Spring Session 可以使用注释的属性来配置,而非之前描述的配置属性。spring-doc.cadn.net.cn

6. Spring for GraphQL

如果你想构建 GraphQL 应用程序,可以利用 Spring Boot 对 Spring for GraphQL 的自动配置功能。 Spring for GraphQL 项目基于 GraphQL Java 开发。 你需要Spring-boot-starter-graphQL至少要先有个初始权。 由于 GraphQL 不依赖于传输,你还需要在应用中添加一个或多个额外的起始程序,以便通过网络发布 GraphQL API:spring-doc.cadn.net.cn

起动机 运输 实现

Spring Boot启动网spring-doc.cadn.net.cn

HTTPspring-doc.cadn.net.cn

春季MVCspring-doc.cadn.net.cn

Spring Boot启动网页接口spring-doc.cadn.net.cn

WebSocketspring-doc.cadn.net.cn

适用于 Servlet 应用的 WebSocketspring-doc.cadn.net.cn

Spring BootStarters网流spring-doc.cadn.net.cn

HTTP,WebSocketspring-doc.cadn.net.cn

春季网络流spring-doc.cadn.net.cn

Spring Boot器启动spring-doc.cadn.net.cn

TCP,WebSocketspring-doc.cadn.net.cn

Reactor Netty 上的 Spring WebFluxspring-doc.cadn.net.cn

6.1. GraphQL 架构

Spring GraphQL 应用程序启动时需要定义的模式。 默认情况下,你可以在下面写入“.graphqls”或“.gqls”模式文件src/main/resources/graphql/**而Spring靴会自动拾取它们。 你可以自定义地点spring.graphql.schema.locations以及文件扩展名spring.graphql.schema.file-extensions.spring-doc.cadn.net.cn

如果你想让 Spring Boot 检测该位置所有应用模块和依赖中的模式文件, 你可以设置spring.graphql.schema.locations“classpath*:graphQL/**/”(注意classpath*:前缀)。

在接下来的章节中,我们将考虑这个示例 GraphQL 模式,定义两种类型和两种查询:spring-doc.cadn.net.cn

type Query {
    greeting(name: String! = "Spring"): String!
    project(slug: ID!): Project
}

""" A Project in the Spring portfolio """
type Project {
    """ Unique string id used in URLs """
    slug: ID!
    """ Project name """
    name: String!
    """ URL of the git repository """
    repositoryUrl: String!
    """ Current support status """
    status: ProjectStatus!
}

enum ProjectStatus {
    """ Actively supported by the Spring team """
    ACTIVE
    """ Supported by the community """
    COMMUNITY
    """ Prototype, not officially supported yet  """
    INCUBATING
    """ Project being retired, in maintenance mode """
    ATTIC
    """ End-Of-Lifed """
    EOL
}
默认情况下,模式中允许现场内视,因为这对GraphiQL等工具是必需的。 如果你不想暴露关于模式的信息,可以通过设置来禁用内省功能spring.graphql.schema.introspection.enabledfalse.

6.2. GraphQL 运行连接

The GraphQL Java运行时Wiring.Builder可用于注册自定义标量类型、指令、类型解析器,数据费彻,以及更多。 你可以申报RuntimeWiringConfigurer在你的 Spring 配置中获取 Beans 以获取运行时Wiring.Builder. Spring Boot 检测到这些豆子并将它们添加到 GraphQlSource 构建器中。spring-doc.cadn.net.cn

然而,通常应用程序不会实现数据费彻直接生成带注释的控制器。 Spring Boot 会自动检测@Controller带有注释处理方法的类,并将其注册为数据费彻s. 这里有一个我们问候查询的示例实现,包含@Controller类:spring-doc.cadn.net.cn

Java
import org.springframework.graphql.data.method.annotation.Argument;
import org.springframework.graphql.data.method.annotation.QueryMapping;
import org.springframework.stereotype.Controller;

@Controller
public class GreetingController {

    @QueryMapping
    public String greeting(@Argument String name) {
        return "Hello, " + name + "!";
    }

}
Kotlin
;

import org.springframework.graphql.data.method.annotation.Argument
import org.springframework.graphql.data.method.annotation.QueryMapping
import org.springframework.stereotype.Controller

@Controller
class GreetingController {

    @QueryMapping
    fun greeting(@Argument name: String): String {
        return "Hello, $name!"
    }

}

6.3. QueryDSL 和 QueryByExample 仓库支持

Spring Data 支持Querydsl和QueryByExample仓库。 Spring GraphQL 可以配置 QueryDSL 和 QueryByExample 仓库为数据费彻.spring-doc.cadn.net.cn

Spring Data 仓库注释为@GraphQlRepository并延伸其中之一:spring-doc.cadn.net.cn

被春季靴检测到并被视为候选对象数据费彻用于匹配顶级查询。spring-doc.cadn.net.cn

6.4. 运输舰

6.4.1. HTTP 和 WebSocket

GraphQL 的 HTTP 端点位于 HTTP POST/graphql默认。 路径可以自定义为spring.graphql.path.spring-doc.cadn.net.cn

Spring MVC 和 Spring WebFlux 的 HTTP 端点均由路由器功能带有@Order0. 如果你定义了自己的路由器功能豆子,你可能想加点合适的@Order注释以确保它们正确排序。

GraphQL WebSocket 端点默认关闭。启用它:spring-doc.cadn.net.cn

Spring GraphQL 提供了 Web 拦截模型。 这对于从HTTP请求头获取信息并将其置于GraphQL上下文中非常有用,或者从同一上下文获取信息并写入响应头部。 使用 Spring Boot,你可以声明网络拦截者Bean希望它能在网络运输系统注册。spring-doc.cadn.net.cn

Spring MVCSpring WebFlux 支持 CORS(跨起源资源共享)请求。 CORS 是从 GraphQL 应用访问时,网络配置中的关键部分,这些应用可通过不同域的浏览器访问。spring-doc.cadn.net.cn

Spring Boot 支持许多配置属性,以下为春.graphql.cors.*Namespace;这里有一个简短的配置示例:spring-doc.cadn.net.cn

性能
spring.graphql.cors.allowed-origins=https://example.org
spring.graphql.cors.allowed-methods=GET,POST
spring.graphql.cors.max-age=1800s
Yaml
spring:
  graphql:
    cors:
      allowed-origins: "https://example.org"
      allowed-methods: GET,POST
      max-age: 1800s

6.4.2. 罗克特

RSocket也被支持作为传输工具,基于WebSocket或TCP。 一旦RSocket服务器配置好,我们可以用以下方式在特定路由上配置GraphQL处理程序spring.graphql.rsocket.mapping. 例如,将该映射配置为“GraphQL”这意味着我们可以用它作为发送请求时的路由,RSocketGraphQlClient.spring-doc.cadn.net.cn

Spring Boot 会自动配置 aRSocketGraphQlClient.Builder<?>你可以注入组件的豆子:spring-doc.cadn.net.cn

Java
@Component
public class RSocketGraphQlClientExample {

    private final RSocketGraphQlClient graphQlClient;

    public RSocketGraphQlClientExample(RSocketGraphQlClient.Builder<?> builder) {
        this.graphQlClient = builder.tcp("example.spring.io", 8181).route("graphql").build();
    }
Kotlin
@Component
class RSocketGraphQlClientExample(private val builder: RSocketGraphQlClient.Builder<*>) {

然后发送请求:spring-doc.cadn.net.cn

Java
Mono<Book> book = this.graphQlClient.document("{ bookById(id: \"book-1\"){ id name pageCount author } }")
    .retrieve("bookById")
    .toEntity(Book.class);
Kotlin
val book = graphQlClient.document(
    """
    {
        bookById(id: "book-1"){
            id
            name
            pageCount
            author
        }
    }               
    """
)
    .retrieve("bookById").toEntity(Book::class.java)

6.5. 异常处理

Spring GraphQL 允许应用程序注册一个或多个 SpringDataFetcherExceptionResolver这些组件是顺序调用的。 该例外必须解析为以下列表GraphQL。GraphQLError对象,请参见 Spring GraphQL 的异常处理文档。 Spring Boot 会自动检测DataFetcherExceptionResolver并向GraphQLSource.Builder.spring-doc.cadn.net.cn

6.6. GraphiQL 和 Schema 打印机

Spring GraphQL 为开发者在使用或开发 GraphQL API 时提供了基础设施。spring-doc.cadn.net.cn

Spring GraphQL 自带一个默认的 GraphiQL 页面,显示于“/graphiql”默认。 此页面默认被禁用,可以通过spring.graphql.graphiql.enabled财产。 许多暴露此类页面的应用程序会更倾向于自定义构建。 默认实现在开发过程中非常有用,这也是为什么它会被自动暴露Spring-boot-devtools在开发过程中。spring-doc.cadn.net.cn

你也可以选择以文本格式暴露 GraphQL 模式,地址为/graphql/schemaspring.graphql.schema.printer.enabled属性已启用。spring-doc.cadn.net.cn

7. 春之仇恨

如果你开发了一个利用超媒体的 RESTful API,Spring Boot 为 Spring HATEOAS 提供自动配置,适用于大多数应用程序。 自动配置取代了使用@EnableHypermediaSupport并注册多个 T豆子,以便于构建基于超媒体的应用程序,包括LinkDiscoverers(用于客户端支持)以及一个对象映射器配置以正确地将响应组织进所需的表示。 这对象映射器通过设置各种参数进行自定义Spring。Jackson。*如果存在,则由Jackson2ObjectMapperBuilder豆。spring-doc.cadn.net.cn

您可以通过以下方式控制 Spring HATEOAS 的配置@EnableHypermediaSupport. 注意,这样做会禁用对象映射器之前描述的定制化。spring-doc.cadn.net.cn

春季Starters仇恨该项目专属于春季MVC,不应与春季WebFlux合并使用。 为了在 Spring WebFlux 中使用 Spring HATEOAS,你可以直接依赖于org.springframework.hateoas:spring-hateoas以及Spring BootStarters网流.

默认情况下,接受application/json将获得application/hal+json响应。 要禁用该行为集Spring.hateoas.use-hal-as-default-json-media-typefalse并定义一个超媒体地图信息HalConfiguration配置 Spring HATEOAS 以满足您的应用程序及其客户端的需求。spring-doc.cadn.net.cn

8. 接下来要读什么

你现在应该已经很好地了解如何用 Spring Boot 开发 Web 应用了。 接下来的几个章节将介绍 Spring Boot 如何与各种数据技术消息系统及其他 IO 功能集成。 你可以根据申请需求选择任何一个。spring-doc.cadn.net.cn