Spring 的每周 roundup 是追踪生态演进节奏的好窗口。即便单周没有重磅版本发布,社区里持续涌出的新工具、配置改进和最佳实践更新,累积起来也在重塑日常开发方式。这篇整理把近期值得留意的方向拎出来,并附上可以直接拿去改造的实操示例。
Spring Boot 启动性能的持续打磨
Spring Boot 的启动耗时一直是微服务和容器化场景下的敏感指标。近几个版本的改动方向很明确:减少 Bean 初始化的阻塞点、延迟不必要的自动配置加载、以及更细粒度的条件评估缓存。
对开发者来说,最直接的收益来自 lazy initialization 和 并行 Bean 初始化 两个开关。前者在 2.2 引入,后者在 3.2 之后逐步成熟。两者组合使用时,冷启动时间在中等规模项目里可以压缩 30%-50%,但代价是首次请求可能触发延迟加载的延迟抖动。
可以这样实践——在 application.yml 中同时开启:
spring:
main:
lazy-initialization: true
boot:
startup:
parallel-bean-initialization:
enabled: true
max-parallel: 4 # 根据 CPU 核数调整,别超过可用核心
注意:
lazy-initialization: true意味着所有单例 Bean 不再在启动阶段创建,而是等到首次注入或调用时才实例化。如果你的应用有定时任务或启动后必须立即就绪的健康检查端点,需要把相关 Bean 标记为@Lazy(false)接口显式排除延迟。
GraalVM Native Image 的生态适配进度
Native Image 编译在 Spring 生态里的推进速度比很多人预期的更快。Spring Framework 6.x 和 Spring Boot 3.x 从架构层面做了大量原生兼容改造:移除运行时字节码生成依赖、提前解析代理配置、将资源路径写进 reachability metadata。
目前大多数主流 Starter 已经提供了 native hint,但第三方库的适配仍然是主要卡点。如果你在项目中用了尚未提供 hint 的库,需要自己补充 reachability metadata。
一个典型的补充方式——为 Jackson 序列化某个不在自动扫描范围内的 DTO:
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
public class MyDtoHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
// 让 Native Image 编译时知道这个类会被反射访问
hints.reflection()
.registerType(TypeReference.of("com.example.dto.OrderResponse"),
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
MemberCategory.INVOKE_PUBLIC_METHODS));
// 如果涉及资源文件读取
hints.resources()
.registerPattern("my-config/*.json");
}
}
然后在 spring.factories 或 Spring Boot 3.x 的 META-INF/spring/aot.factories 中注册:
org.springframework.aot.hint.RuntimeHintsRegistrar=com.example.config.MyDtoHints
编译和验证命令:
# 使用 Spring Boot 的 native 编译插件(需安装 GraalVM JDK)
./mvnw -Pnative native:compile
# 直接运行生成的二进制
./target/my-app
# 对比 JVM 模式的启动时间
./mvnw spring-boot:run
实测中,一个包含 5 个 REST 端点的中等项目,JVM 模式冷启动约 2.8 秒,Native Image 约 0.06 秒。但编译耗时 2-4 分钟,内存占用也更固定——适合短生命周期容器和 Serverless 场景,不适合长时间运行且需要动态类加载的传统服务。
Spring Security 的配置简化趋势
Spring Security 的 Java 配置方式从 WebSecurityConfigurerAdapter 淬变到组件式 SecurityFilterChain Bean 已经是既定事实。近期的变化更偏向 方法级安全的细粒度控制 和 OAuth2/OIDC 集成的开箱即用度。
一个实用的模式:用 @PreAuthorize 结合自定义 SecurityExpression 方法,把权限判断从硬编码角色名解放出来:
@Configuration
@EnableMethodSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
}
// 自定义表达式根,注入到 Security 评估上下文
@Component("perm")
public class PermissionEvaluator {
private final OrderRepository orders;
public PermissionEvaluator(OrderRepository orders) {
this.orders = orders;
}
/** 判断当前用户是否是订单的所有者 */
public boolean ownsOrder(Long orderId) {
String currentUser = SecurityContextHolder.getContext()
.getAuthentication().getName();
return orders.findById(orderId)
.map(order -> order.getOwner().equals(currentUser))
.orElse(false);
}
}
Controller 中直接调用:
@RestController
@RequestMapping("/orders")
public class OrderController {
@GetMapping("/{id}")
@PreAuthorize("@perm.ownsOrder(#id)")
public OrderResponse getOrder(@PathVariable Long id) {
// 只有订单所有者能访问,不需要在 URL 匹配层硬写角色
return orderService.findById(id);
}
}
这种模式比在 HttpSecurity 里堆 requestMatchers 更灵活,也更容易做单元测试——PermissionEvaluator 是普通 Spring Bean,可以脱离 Security 上下文独立测试逻辑。
Observability:从 Micrometer Tracing 到 OpenTelemetry 的对齐
Spring Boot 3.x 把分布式追踪从 Spring Cloud Sleuth 迁移到 Micrometer Tracing + OpenTelemetry SDK,这是很多人迁移时踩坑的地方。关键变化:Sleuth 的 spring.sleuth.* 配置全部失效,需要改用 management.tracing.* 和 OTel SDK 的原生配置。
一个最小可用的 OTel + Zipkin 导出配置:
management:
tracing:
sampling:
probability: 1.0 # 开发环境全采样,生产环境降到 0.1
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
otel:
exporter:
otlp:
endpoint: http://localhost:4317 # OTLP gRPC receiver
resource:
attributes:
service.name: my-order-service
依赖变更(Maven):
<!-- 移除 sleuth,替换为 micrometer + otel -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
<!-- Zipkin 用 OTLP 协议对接,不再需要专门的 zipkin sender -->
本地验证:
# 启动 Zipkin(支持 OTLP ingestion)
docker run -d -p 9411:9411 -p 4317:4317 openzipkin/zipkin-otel
# 启动应用后访问任意端点
curl http://localhost:8080/orders/1
# 在 Zipkin UI 查看追踪
open http://localhost:9411
实用检查清单
在跟进 Spring 生态更新时,以下几项值得每次 roundup 后快速扫一遍:
| 检查项 | 为什么重要 |
|---|---|
Boot 版本的 spring.main.lazy-initialization 和并行初始化开关 |
直接影响冷启动,容器调度场景敏感 |
| 项目中第三方库的 Native Image hint 覆盖情况 | 决定能否顺利编译 native 二进制 |
@PreAuthorize / @PostAuthorize 的使用是否还依赖废弃 API |
方法级安全是当前推荐方式 |
| Sleuth → Micrometer Tracing 的迁移是否完成 | Boot 3.x 不再维护 Sleuth |
spring.factories → AutoConfiguration.imports 的迁移 |
3.x 自动配置注册机制已切换 |
Spring 的演进节奏是"小步快跑"式的——单个版本改动不大,但方向一致。每周花 10 分钟扫 roundup,比半年后被迫批量迁移要轻松得多。