SpringBoot3新特性

支持版本

jdk 17+
maven 3.5+
tomcat 10.1+

1.自动配置文件变更

在SpringBoot 3中,自动配置包的位置发生了变化。在SpringBoot 2.X版本中,自动配置包位于META-INF/spring.factories

而在SpringBoot 3.X版本中,它们被移动到了META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

2.Jakarta API迁移

SpringBoot 3对Jakarta API进行了迁移,这意味着一些包名和依赖项发生了变化。

例如,SpringBoot 3使用了新的druid-spring-boot-3-starter依赖项来解决与Druid整合的兼容性问题。

3.新特性 - ProblemDetails

SpringBoot 3引入了一个新规范,即当发生某些异常时,以RFC 7807规范方式返回错误数据。这是通过ProblemDetailsErrorHandlingConfiguration类实现的,它是一个@ControllerAdvice,用于集中处理系统异常。

  • RFC 7807定义了为HTTP响应中错误的可读详细信息,以避免需要为HTTP API定义新的错误响应格式。HTTP [RFC7230]状态码有时不足以传达关于错误的足够信息。

  • RFC 7807 定义了简单的JSON[RFC7159]和XML[W3C.REC-XML-20081126]文档格式以满足此目的。它们被设计为可由HTTP API重用,HTTP API可以识别特定于其需求的不同“问题类型”。

  • 因此,API客户端既可以知道高级错误类(使用状态码),也可以知道问题的细粒度细节。

  • 例如,考虑一个响应,该响应表明客户的账户没有足够的权限。403禁止状态代码可能被认为是最适合使用的,因为它将向HTTP通用软件(如客户端库、缓存和代理)通知响应的一般语义。然而,这并没有为API客户端提供足够的信息,说明为什么禁止请求、适用的帐户余额或如何纠正问题。如果这些细节以可读的格式包含在响应体中,则客户端可以适当地处理它;例如触发将更多的信用转移到账户中。

  • RFC 7807规范通过使用URI[RFC3986]识别特定类型的问题(例如,“信用不足”)来实现这一点;HTTP API可以通过指定受其控制的新URI或重用现有URI来实现这一点。

  • 此外,Problem Detail信息可以包含其他信息,例如标识问题具体发生的URI(有效地为“Joe上周四没有足够的信用”这一概念提供了标识符),这对于支持或取证目的可能很有用。

  • Problem Detail的数据模型是一个JSON[RFC7159]对象;当格式化为JSON文档时,它使用“application/problem+json”媒体类型。

请注意,Problem Detail 不是在HTTP中传达问题细节的唯一方式;例如,如果响应仍然是资源的表示,那么通常最好以该应用程序的格式来描述相关细节。同样,在许多情况下,有一个适当的HTTP状态代码,不需要传递额外的细节。

Problem Detail消息格式

Problem Detail的规范模型是JSON对象。当序列化为JSON文档时,该格式用“application/problem+json”媒体类型标识。

例如,一个带有JSONProblem Detail的HTTP响应:

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: en
{
"type": "https://pack.com/probs/out-of-credit",
"title": "你没有足够的信用。",
"detail": "你现在的积分是10,但是要花20。",
"instance": "/account/12345/msgs/abc",
"balance": 10,
"accounts": ["/account/12345", "/account/67890"]
}

这里,结余不足问题(由其类型URI标识)

type:标识问题类型的URI引用

title:中指明了403的原因

instance:给出了具体问题发生的参考

detail:中给出了发生的具体细节,并添加了两个扩展

balance:表示帐户的积分

accounts:提供了可以充值帐户积分的链接

4.新特性 - 函数式接口(WebFlux)

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

SpringBoot 3支持使用函数式接口来定义Web请求处理流程。这种方法允许开发者以分离式的方式(路由和业务分离)来处理Web请求,使用RouterFunctionRequestPredicate等核心类来定义路由信息和请求规则。

Spring WebFlux 有两种类型:函数式和基于注释的。基于注释的非常接近 Spring MVC 模型,如以下示例所示:

普通调用

@GetMapping("/hello")
public String hello(String name) {
return "Hello World"+name;
}

函数式风格 (Spring Boot 3.4)

首先,你需要确保启用了 Spring WebFlux 来支持函数式 API。然后,你可以使用 RouterFunction 和 HandlerFunction 来定义路由和处理函数。

pom

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
package com.lazy.handler;

@Component
public class HelloHandler {

//接受restful风格的参数
public Mono<ServerResponse> hello(ServerRequest request) {
String id = request.pathVariable("id");
return ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.body(Mono.just("hello world"+id), String.class);
}
//接受请求路径的参数
public Mono<ServerResponse> query(ServerRequest request) {
String search = request.queryParam("search").orElse("默认用户");
return ServerResponse.ok()
.contentType(MediaType.TEXT_PLAIN)
.body(Mono.just("hello world"+search), String.class);
}
//接受请求体的参数
public Mono<ServerResponse> getAll(ServerRequest request) {
Mono<Bean> beanMono = request.bodyToMono(Bean.class);
return beanMono.flatMap(query -> {
String result = "用户名:" + query.getName() + ",年龄:" + query.getAge();
// 响应格式改为 APPLICATION_JSON(与返回数据匹配,NDJSON 适合流数据,此处没必要)
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(result); // 直接返回字符串(或包装为 JSON 对象)
});
}
}
package com.lazy.config;

import com.lazy.handler.HelloHandler;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.*;

import static org.springframework.web.reactive.function.server.RequestPredicates.GET;

@Configuration
public class RouterConfig {

@Resource
private HelloHandler helloHandler;

@Bean
public RouterFunction<ServerResponse> helloRoute() {
RequestPredicate accept = RequestPredicates.accept(MediaType.valueOf(String.valueOf(MediaType.APPLICATION_NDJSON)));
return RouterFunctions
.route(GET("/hello/{id}")//表示匹配 HTTP GET 请求,并将其映射到 /hello 路径。
.and(accept),//表示只接受文本格式的请求。
helloHandler::hello)
.andRoute(
RequestPredicates.GET("/hello")
.and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
helloHandler::query
).andRoute(
RequestPredicates.POST("/hello")
.and(RequestPredicates.accept(MediaType.APPLICATION_JSON)),
helloHandler::getAll
);

}
}

image-20251117223125885