Zademy

Spring Boot 4 和 Spring Framework 7:Java 微服务快速安全的新时代

Java
SpringBoot; Java
2840 字

你好,开发者社区!Spring Boot 4.0 的发布(自 2025 年 11 月起可用),以及 Spring Framework 7.0,标志着 Java 生态系统中的一个重要里程碑。这不仅仅是一次增量更新,而是朝着现代、模块化、云就绪和注重性能的 Java 架构迈出的决定性一步。

如果你想要优化性能、简化代码并让你的应用程序在设计上更安全,这个版本有你需要的工具。在这里,我们深入分析最重要的变化。

最低要求:拥抱现代性

Spring Boot 4 继续现代化进程,提高了技术栈以确保更高效的平台:

  • Java (JDK):最低要求仍然是 Java 17。然而,强烈建议使用 Java 21 或更高版本(最好是 Java 25)以利用 JVM 的最新特性,如 Virtual Threads(虚拟线程),它们旨在竞争并可能取代传统的响应式编程(WebFlux)。
  • Jakarta EE 11:与 Jakarta EE 11 的对齐是完整的,这意味着采用更新的技术,如 Servlet 6.1、JPA 3.2 和 Bean Validation 3.1。
  • Kotlin:现在需要 2.2 或更高版本
  • GraalVM:完全支持 GraalVM 24 或更高版本,优化原生镜像生成。

性能的巨大飞跃:模块化和 AOT

最显著的改进之一,虽然在表面上看不出来,是 Spring Boot 核心的模块化

模块化:拆开行李箱

以前,spring-boot-autoconfigure 模块是一个巨大的工件,包含几乎所有内容的配置逻辑,即使你没有使用它。这产生了所谓的 "classpath noise"(类路径噪音)。

解决方案:Spring Boot 4 已将这个巨大的模块拆分为更小、更专注的工件。现在,自动配置分布在专门的模块中,只有在需要时才加载。

具体好处

  1. 更快的启动:扫描的代码更少意味着启动时间减少。
  2. 更低的内存消耗:只加载你真正需要的内容。
  3. 更好的开发体验:你的 IDE 会给你更精确的建议,不会显示无关的类和属性。
  4. 优化的原生镜像:使用 GraalVM 的原生镜像生成更高效。

AOT 编译和 GraalVM

Spring Boot 4 完全与 GraalVM 24 或更高版本对齐。AOT(Ahead-of-Time)处理已显著优化:

  • 更快的编译时间
  • 启动时内存占用减少
  • 更好的静态分析以消除死代码
  • 改进的 reflection hints 兼容性

新的 @ConfigurationPropertiesSource 注解允许更细粒度的模块化,使只有相关的配置属性被处理。

空值安全:NullPointerException 的终结

历史上,Java 中的 null 处理一直是生产环境中错误的持续来源。Spring Framework 7 和 Spring Boot 4 已在整个产品组合中采用 JSpecify 作为空值安全(Null Safety)的标准。

什么是 JSpecify?

JSpecify 是一个由 Google、JetBrains、Meta、Oracle 和其他科技巨头支持的协作标准。它提供明确的注解来指示值是否可以为 null 或不能

注解含义类比
@Nullable此值可以缺失红灯(停止并检查!)
@NonNull此值从不为 null绿灯(继续前进!)
@NullMarked建立默认规则:除非另有说明,否则所有内容都是 NonNull安全区

游戏规则改变者:@NullMarked

你可以通过创建一个 package-info.java 文件将整个包标记为空值安全:

// src/main/java/com/tuempresa/servicio/package-info.java
@org.jspecify.annotations.NullMarked
package com.tuempresa.servicio;

有了这个,你的 IDE(如支持它的 IntelliJ IDEA 2025.3+)会在你尝试在期望非空值的上下文中使用可能为 null 的值时,在你编写代码时提醒你。

实际示例

@NullMarked
package com.ejemplo.usuarios;

public class UsuarioService {

    // IDE 会在你尝试传递 null 时警告
    public Usuario crearUsuario(@NonNull String nombre, @Nullable String apellido) {
        // nombre 永不为 null - 可以直接安全使用
        String nombreCompleto = nombre.toUpperCase();

        // apellido 可能为 null - 你必须检查
        if (apellido != null) {
            nombreCompleto += " " + apellido.toUpperCase();
        }

        return new Usuario(nombreCompleto);
    }
}

这大大降低了生产环境中 NullPointerException 的风险,在开发时就检测到问题。

声明式 HTTP 客户端:Feign 的"杀手"

Spring Boot 4 通过声明式 HTTP 客户端简化了 service-to-service 通信,这是一个备受期待的功能,消除了对外部依赖如 Spring Cloud OpenFeign 的需求。

告别 RestTemplate

RestTemplate 在 Spring Framework 7 中已被正式弃用,将在未来版本中移除。代码冗长、难以测试,且未利用 Java 的现代特性。

新时代:RestClient 和 @HttpExchange

RestClientRestTemplate 的现代替代品,具有流畅的 API 和 builder 风格。但最革命性的是使用 @HttpExchange 创建声明式 HTTP 客户端的能力。

声明式客户端示例

package com.tuempresa.clients;

import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.annotation.PostExchange;
import org.springframework.web.service.annotation.HttpExchange;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;

@HttpExchange("/api/productos")
public interface ProductoClient {

    @GetExchange("/{id}")
    Producto obtenerProducto(@PathVariable("id") String id);

    @GetExchange
    List<Producto> listarProductos();

    @PostExchange
    Producto crearProducto(@RequestBody Producto producto);
}

客户端配置

@Configuration
public class ClientConfig {

    @Bean
    public ProductoClient productoClient(RestClient.Builder builder) {
        RestClient restClient = builder
            .baseUrl("https://api.ejemplo.com")
            .build();

        HttpServiceProxyFactory factory = HttpServiceProxyFactory
            .builderFor(RestClientAdapter.create(restClient))
            .build();

        return factory.createClient(ProductoClient.class);
    }
}

优势

  • 零样板代码:定义调用什么,而不是如何调用
  • 更易测试:你可以轻松模拟接口
  • 类型安全:利用 Java 的类型系统
  • 原生集成:不需要额外的依赖

Spring Boot 4 还在自动配置这些客户端方面进行了改进,使用 clientType 属性允许在 RestClient(默认)和 WebClient(用于响应式情况)之间切换,而无需修改客户端代码。

弹性和 API 版本控制

此版本将关键的架构模式直接集成到框架的核心中。

原生弹性

弹性注解,以前依赖于外部的 Spring Retry 项目,现在已集成到 Spring Framework 7 中:

  • @Retryable:如果方法失败,应用重试逻辑
  • @ConcurrencyLimit:限制对方法的并发调用
  • @EnableResilientMethods:在你的 beans 中激活这些注解

弹性示例

@Service
@EnableResilientMethods
public class PagoService {

    @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000))
    public PagoRespuesta procesarPago(PagoRequest request) {
        // 如果失败,最多重试 3 次,等待 1 秒
        return pagoGateway.procesar(request);
    }

    @ConcurrencyLimit(maxConcurrency = 10)
    public Report generarReporte() {
        // 最多 10 个并发调用
        return reportService.generar();
    }
}

原生 API 版本控制

Spring Framework 7 解决了 REST API 版本控制的"噩梦"。现在你可以在 @RequestMapping 及其变体中使用 version 属性:

带版本控制的 Controller 示例

@RestController
@RequestMapping("/api/pedidos")
public class PedidoController {

    // 版本 1:简单响应
    @GetMapping(version = "1", produces = MediaType.APPLICATION_JSON_VALUE)
    public List<PedidoV1> getPedidosV1() {
        return pedidoService.obtenerPedidosSimples();
    }

    // 版本 2:带有元数据的丰富响应
    @GetMapping(version = "2", produces = MediaType.APPLICATION_JSON_VALUE)
    public PedidoResponseV2 getPedidosV2() {
        return pedidoService.obtenerPedidosCompletos();
    }
}

集中配置

@Configuration
public class ApiVersioningConfig implements WebMvcConfigurer {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        // 基于 header 的策略
        configurer.apiVersioning(builder -> builder
            .strategy(VersionStrategy.HEADER)
            .headerName("API-Version")
        );

        // 或基于路径:/v1/pedidos, /v2/pedidos
        // 或基于查询参数:/pedidos?version=1
    }
}

版本控制策略是集中定义的,这让你可以在不重写 controllers 的情况下更改策略(路径、header、查询参数)。

高保真可观测性

对于 cloud-native 应用,监控能力至关重要。Spring Boot 4 显著改进了可观测性。

与 OpenTelemetry 的集成

  • Micrometer 2.0:更新并改进了性能
  • 原生 OpenTelemetry:Spring Boot 4 中包含官方 starter
  • 自动关联:日志、指标和追踪自动关联

@Observed 注解

为任何方法自动生成指标和追踪:

@Service
public class InventarioService {

    @Observed(name = "inventario.verificar", contextualName = "verificarDisponibilidad")
    public boolean verificarDisponibilidad(String productoId) {
        // 自动生成以下指标:
        // - 执行时间
        // - 调用次数
        // - 错误率
        return inventarioRepository.existeStock(productoId);
    }
}

SSL 健康监控

Actuator 现在对 SSL 证书执行自动监控。如果证书即将过期(默认在 14-30 天内),/actuator/health 端点会明确报告:

{
  "status": "UP",
  "components": {
    "ssl": {
      "status": "WARNING",
      "details": {
        "certificate": "api.ejemplo.com",
        "expiresIn": "12 days",
        "expiryDate": "2025-12-05"
      }
    }
  }
}

这对于避免生产环境中证书意外过期时的恐慌是一个"救生圈"。

核心变化:Client 模式

如果你使用过 Spring,你可能使用过 Templates(例如 JdbcTemplateRestTemplateJmsTemplate)。Spring Framework 7 正在积极地从 "Template Method" 模式迁移到基于 builders 和函数式接口的新Client 模式

旧模式新模式(替代/选项)状态
RestTemplateRestClient / @HttpExchange已弃用
JdbcTemplate / NamedParameterJdbcTemplateJdbcClient共存
JmsTemplateJmsClient新增

JdbcClient:更清洁的代码

新的 JdbcClient 消除了冗长,采用了更直观且易于阅读的 builder 模式:

之前(JdbcTemplate)

public List<Persona> findAll() {
    return jdbcTemplate.query(
        "SELECT id, nombre, edad FROM persona",
        new RowMapper<Persona>() {
            @Override
            public Persona mapRow(ResultSet rs, int rowNum) throws SQLException {
                Persona p = new Persona();
                p.setId(rs.getLong("id"));
                p.setNombre(rs.getString("nombre"));
                p.setEdad(rs.getInt("edad"));
                return p;
            }
        }
    );
}

现在(JdbcClient)

public List<Persona> findAll() {
    return jdbcClient.sql("SELECT id, nombre, edad FROM persona")
        .query((rs, rowNum) -> new Persona(
            rs.getLong("id"),
            rs.getString("nombre"),
            rs.getInt("edad")
        ))
        .list();
}

// 或者使用自动映射更简单
public List<Persona> findAll() {
    return jdbcClient.sql("SELECT * FROM persona")
        .query(Persona.class)
        .list();
}

带参数的查询

public Optional<Persona> findById(Long id) {
    return jdbcClient.sql("SELECT * FROM persona WHERE id = :id")
        .param("id", id)
        .query(Persona.class)
        .optional();
}

写操作

public int actualizarEdad(Long id, int nuevaEdad) {
    return jdbcClient.sql("UPDATE persona SET edad = :edad WHERE id = :id")
        .param("edad", nuevaEdad)
        .param("id", id)
        .update();
}

这种新方法:

  • 更简洁易读
  • 利用 lambdas 和方法引用
  • Optional 更好地集成以安全处理空值
  • 改进对 AOT 和 GraalVM 的支持

测试改进

Spring Boot 4 在测试能力方面引入了显著改进:

更细粒度的测试切片

新的测试切片用于测试特定组件,而无需加载整个应用程序上下文:

@HttpServiceClientTest
class ProductoClientTest {

    @Autowired
    private ProductoClient productoClient;

    @Test
    void deberiaObtenerProducto() {
        // 只加载 HTTP 客户端所需的配置
        Producto producto = productoClient.obtenerProducto("123");
        assertThat(producto).isNotNull();
    }
}

改进的 TestRestTemplate

与新的 RestClient 更好的集成以及对类型化响应的支持:

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class IntegrationTest {

    @Autowired
    private TestRestClient restClient;

    @Test
    void deberiaCrearUsuario() {
        Usuario nuevoUsuario = new Usuario("Juan", "Pérez");

        Usuario creado = restClient.post()
            .uri("/api/usuarios")
            .body(nuevoUsuario)
            .exchange()
            .expectStatus().isCreated()
            .expectBody(Usuario.class)
            .returnResult();

        assertThat(creado.getId()).isNotNull();
    }
}

从 Spring Boot 3 迁移

从 Spring Boot 3 迁移到 Spring Boot 4 需要关注几个要点:

强制更改

  1. Jackson 3.x:从 Jackson 2.x 更新到 3.x
  2. Jakarta 命名空间:从 javax.*jakarta.* 的完整迁移
  3. Java 17 最低要求:虽然建议使用 Java 21+
  4. Kotlin 2.2+:如果你使用 Kotlin

重要的弃用

  • RestTemplateRestClient 或声明式客户端
  • Spring Security 中 lambda 配置是强制性的
  • 多个配置属性已更改名称

迁移工具

Spring 建议使用 OpenRewrite 来自动化大部分迁移:

<plugin>
    <groupId>org.openrewrite.maven</groupId>
    <artifactId>rewrite-maven-plugin</artifactId>
    <version>5.42.0</version>
    <configuration>
        <activeRecipes>
            <recipe>org.openrewrite.java.spring.boot4.UpgradeSpringBoot_4_0</recipe>
        </activeRecipes>
    </configuration>
</plugin>

此插件可以自动化:

  • 依赖更新
  • javax → jakarta 命名空间更改
  • 已弃用 API 的迁移
  • 配置文件的更新

结论:为什么迁移到 Spring Boot 4?

Spring Boot 4 和 Spring Framework 7 旨在使 Java 应用程序更轻量、更可预测、更快、更安全

关键好处

  1. 性能:模块化和 AOT 显著改善启动时间和内存消耗
  2. 安全性:JSpecify 减少生产环境中的 NPE
  3. 生产力:更清洁、更声明式的 API 减少样板代码
  4. 弹性:原生集成的弹性模式
  5. 可观测性:一流的监控和追踪
  6. 云原生:为容器和云环境优化

现代建筑师的隐喻

将 Spring Boot 4 想象成一个现代建筑建筑师。以前你有一个沉重的工具箱,几乎所有东西都是可选的。现在你有专门且轻量的工具,有清晰的说明(JSpecify)、集成的安全机制(弹性)和优化的建筑材料(模块化),结果是建造更快且本质上更稳定的建筑(应用程序)。

迁移值得吗?

是的,绝对值得。虽然这不是微不足道的(需要更新依赖和调整配置),但生产力、性能和可维护性的收益使这种转变成为一项快速回报的投资。

Spring Boot 4 和 Spring Framework 7 不仅仅是一个版本更新,它们是未来十年企业 Java 开发的基础。如果你正在为未来构建应用程序,这应该是你构建的平台。