最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Spring 7.0更新特性大全,一次全学会

网站源码admin4浏览0评论

Spring 7.0更新特性大全,一次全学会

写在前面

Spring 7.0暂未发布正式版(要等),特性可能随着时间变化而变化。

计划兼容JDK 17-27,提升了一部分依赖的组件版本。

比如Tomcat,最低要求Tomcat 11(现在一般都是Spring Boot,几乎不需要自己关注Tomcat)。

话不多说,下面进入正题。

移除Spring JCL

这个版本中spring-jcl这个包被移除了。

替代选项是 Apache Commons Logging 1.3.0 ,对于应用开发来说,没有太大影响。

移除了对javax.annotation和javax.inject注解的支持

javax.annotation 和 javax.inject 包中的注释不再受支持。

这包括 @javax.annotation.Resource、

@javax.annotation.PostConstruct、@javax.inject.Inject 等注释。

如果您的应用程序仍然使用这些包中的注释,您需要迁移到 jakarta.annotation 和 jakarta.inject 包中的等效注释。

大概是升级Spring Boot 3.0的时候,Servlet的依赖包就已经变了(换成jakarta了)。

路径映射选项被移除

变更如下:

1. 后缀模式匹配

(suffixPatternMatch/registeredSuffixPatternMatch)

通过URL后缀匹配请求(如.json、.xml),移除后@RequestMapping("/user.{format}")不再匹配/user.json。

修复办法,改用内容协商(Content Negotiation)或显式路径定义。

历史代码:

代码语言:javascript代码运行次数:0运行复制
@GetMapping("/user.{format}") // 匹配/user.json或/user.xml
public User getUser(@PathVariable String format) {
    return new User("张三");
}

新方案:

代码语言:javascript代码运行次数:0运行复制
// 通过请求参数或Accept头协商内容类型
@GetMapping(value = "/user", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public User getUser(@RequestHeader("Accept") String accept) {
    return new User("张三");
}

2. 结尾斜杠匹配(trailingSlashMatch)

控制是否匹配结尾的斜杠(如:/user和/user/),移除之后路径/user和/user/被视为不同请求。

修复方案可以直接使用一个,或者一次定义两个路径。

如下:

代码语言:javascript代码运行次数:0运行复制
@GetMapping({"/user", "/user/"})
public User getUser() {
    return new User("张三");
}

3. 路径扩展内容协商

(favorPathExtension/ignoreUnknownPathExtensions)

根据URL扩展名(如.json)决定响应格式。移除之后,/user.json不再自动返回JSON。

修复方案,用@RestController注解。

4. 可选结尾分隔符

(matchOptionalTrailingSeparator)

在路径模式中允许可选的结尾分隔符(如/user/?),修复方案,使用正则表达式定义请求路径。

OkHttp3支持

官方提供了OkHttp3支持。

但更多时候,我在使用Retrofit。

Retrofit基于OkHttp封装,通过注解(如 @GET、@POST)简化接口定义。

自动将 HTTP 响应转换为 Java/Kotlin 对象(如 Gson 解析)。

Retrofit接口定义示例:

代码语言:javascript代码运行次数:0运行复制
public interface GitHubService{

  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);

  @GET("group/{id}/users")
  Call<List<User>> groupList(@Path("id") int groupId, @Query("sort") String sort);
  
  @POST("users/new")
  Call<User> createUser(@Body User user);

  @FormUrlEncoded
  @POST("user/edit")
  Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);
}

调用示例:

代码语言:javascript代码运行次数:0运行复制
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");

弃用部分xml配置

弃用了以<mvc:*开头的XML配置命名空间,需要使用Java配置。

但<bean>不会被弃用。

空安全

Spring框架中的JSR 305语义的空值注解已被弃用,需要使用JSpecify。

具体是下面这几个注解:

@Nullable 、 @NonNull 、 @NonNullApi 和 @NonNullFields

虽然距离被彻底删除还有一段时间,但可以先看一下JSpecify。

官方给出的弃用理由是:

  • Spring的注解仅适用于字段、参数、返回值。
  • JSpecify注解在Spring注解基础上增加了类型、类。

JSpecify的例子:

代码语言:javascript代码运行次数:0运行复制
// @Nullable: 返回值可能为空
// @NonNull: 入参不能为空
static @Nullable String emptyToNull(@NonNull String x) {
  return x.isEmpty() ? null : x;
}
// @Nullable: 入参可空
// @NonNull: 返回值不能为空
static @NonNull String nullToEmpty(@Nullable String x) {
  return x == null ? "" : x;
}
// @NullMarked:
//
// 一旦某个类或包被标记为@NullMarked,则其中所有未使用@Nullable明确标记的参数、返回值或字段都将被视为不可为null。
// 在代码中,@NullMarked应用于整个方法声明,意味着该方法内部的所有类型使用(如参数list和toRemove)若未特别标注,则默认不可为null,除非显式使用@Nullable
// 
@NullMarked
public static <T> List<@Nullable T> nullOutMatches(List<T> list, T toRemove) {
  List<@Nullable T> copy = new ArrayList<>(list);
  for (int i = 0; i < copy.size(); i++) {
    if (copy.get(i).equals(toRemove)) {
      copy.set(i, null);
    }
  }
  return copy;
}

HttpHeaders更改

在Spring 7.0中,HttpHeaders类不再继承MultiValueMap接口 。

原因 :

HTTP头信息本质是不区分大小写的键值对集合 ,而传统的Map类操作(如get()/put())在以下场景表现不佳:

1. 性能问题 :MultiValueMap的哈希表结构对大小写不敏感的头字段匹配效率低下;

2. 语义冲突 :Map的键唯一性约束与HTTP头允许重复键(如Set-Cookie)的特性矛盾。

类文件API修改

这个特性跟普通CRUD关系不大。

Spring 通过读取类字节码来收集代码元数据。

之前他们使用了一个简化的 ASM 分支来实现这一目的,

通过

org.springframework.core.type.classreading

包中的 MetadataReaderFactory 和 MetadataReader 类型。

尽管基于 Spring 应用程序通常不会直接接触到这个 API,但在解析 @Configuration 类或查找应用程序代码上的注解时,这特别有用。

Java 24 引入了一个新的类文件API,用于读取和写入类作为Java字节码。

Spring Framework 7.0 采用这一特性。

针对Java 24+(注意:JDK 24非长期支持版本)应用程序,在spring-core中实现了新的ClassFileMetadataReader。

API版本支持

从7.0版本开始,服务器Web应用程序可以通过在@RequestMapping注解中声明版本范围。

将特定版本的路径映射到不同的控制器方法。

版本可以从多个来源解析,如请求URL路径、头部值等。

代码语言:javascript代码运行次数:0运行复制
@GetMapping(value = "/query", version = "1.0.0")
public ResponseEntity<String> queryV1() {
    return ResponseEntity.ok("query api v1.0.0...");
}

@GetMapping(value = "/query", version = "2.0.0")
public ResponseEntity<String> queryV2() {
    return ResponseEntity.ok("query api v2.0.0...");
}

当客户端请求 API 时,若 v 参数值与 version 定义的值匹配,则该接口会被调用。例如:

  • 访问 /api/query?v=1.0.0,返回 query api v1.0.0...
  • 访问 /api/query?v=2.0.0,返回 query api v2.0.0...

若请求的 v 参数不匹配任何版本,则会抛出 InvalidApiVersionException。

HTTP接口代理

HttpServiceProxyFactory 使得创建 HTTP 接口的代理变得简单。

一句话来说,就是可以将你的Service直接发布成接口。

示例:

代码语言:javascript代码运行次数:0运行复制
// 1. 定义多个HTTP服务接口
@HttpExchange(url = ";)
public interface OrderService {
    @PostExchange("/orders")
    Order createOrder(@RequestBody OrderRequest request);
}

@HttpExchange(url = ";)
public interface PaymentService {
    @GetExchange("/payments/{id}")
    Payment getPayment(@PathVariable String id);
}

// 2. 自动注册代理Bean(Spring Boot 3+)
@Configuration
public class HttpConfig {
    @Bean
    public OrderService orderService(WebClient.Builder builder) {
        return HttpServiceProxyFactory.builder(builder.baseUrl(";).build())
            .build()
            .create(OrderService.class);
    }
    // PaymentService同理...
}

更优雅的Bean注册

老代码,有时候会在@Configuration类中的单个@Bean方法内尝试注册多个Bean。

代码不优雅,又影响逻辑。

因此这个版本增加了一个特性,可以更灵活的注册Bean。

示例代码如下:

代码语言:javascript代码运行次数:0运行复制
@Configuration
@Import(MyBeanRegistrar.class)
class MyConfiguration {
}

class MyBeanRegistrar implements BeanRegistrar {
     @Override
     public void register(BeanRegistry registry, Environment env) {
         registry.registerBean("foo", Foo.class);
         registry.registerBean("bar", Bar.class, spec -> spec
                 .prototype()
                 .lazyInit()
                 .description("Custom description")
                 .supplier(context -> new Bar(context.bean(Foo.class))));
         if (env.matchesProfiles("baz")) {
             registry.registerBean(Baz.class, spec -> spec
                     .supplier(context -> new Baz("Hello World!")));
         }
     }
}

看起来还挺简单的。你觉得呢?

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-04-24,如有侵权请联系 cloudcommunity@tencent 删除代理接口配置注解spring
发布评论

评论列表(0)

  1. 暂无评论