Java / Spring Boot:JVM、依赖装配与虚拟线程
核验日期:2026-07-16。本文以 Spring Boot 4.1、Spring Framework 7、Java 25 LTS/Java 26 兼容窗口作为观察范围。官方系统要求显示 Boot 4.1 至少 Java 17、支持到 Java 26;生产选择组织受支持 JDK,并核对 Spring 商业/开源支持窗口。
Spring Boot 的价值在于把 Spring 容器、Web、验证、事务、数据访问、Actuator、配置与部署约定组合成可治理的平台,注解数量并不重要。代价则是启动/内存开销、隐式装配、代理边界和升级面。前端开发者要从“new/函数调用”转向 bean lifecycle、类型擦除/序列化、线程上下文、声明式事务和 JVM 诊断,同时避免为了“企业级”制造没有业务含义的分层。
请求链
Boot 启动时构建 ApplicationContext,依据 classpath、properties 和 conditions 选择 auto-configuration,创建 bean、代理、server。请求期 Servlet MVC 通常一请求一平台/虚拟线程;WebFlux 在 event loop 上推进 reactive pipeline。两种模型不能只换 starter 就混用阻塞驱动。
最小 MVC 契约
record Technology(String id, String name, String category) {}
record TechnologyPage(List<Technology> items, String dataVersion) {}
@RestController
@RequestMapping("/technologies")
final class TechnologyController {
private final CatalogService catalog;
TechnologyController(CatalogService catalog) { this.catalog = catalog; }
@GetMapping
TechnologyPage list(
@RequestParam(defaultValue = "") @Size(max = 100) String query,
@RequestParam(defaultValue = "20") @Min(1) @Max(100) int limit) {
return catalog.list(query, limit);
}
}环境 Java 25、Boot 4.1。需要在 controller/class 启用方法参数验证或使用合适的 validation integration;不同 Boot/Framework 版本行为要测试。合法请求返回 record JSON,limit=101 由统一 exception handler 映射 400 problem。Controller 只适配协议,service 不接 servlet request。
Jackson 是协议边界
Java 类型不自动固定 JSON。Jackson naming、unknown properties、creator、record、null、Optional、时间、BigInteger/BigDecimal、enum 都需策略。输入 DTO 与 JPA entity 分开,防 mass assignment、lazy proxy 和内部字段泄露。输出显式 DTO,contract test 完整 body。
Optional 适合 return 表达缺失,不应无差别用于 entity/DTO fields。PATCH 的 missing/null 使用 JsonNullable、自定义 command 或字段集合明确。时间用 Instant/OffsetDateTime 与 ISO 8601,禁止系统默认 timezone 悄然介入。大整数对 JS 用字符串契约。
Bean Validation 与业务不变量
Jakarta Bean Validation 适合字段/对象结构:长度、范围、格式、跨字段纯规则。数据库是否存在、余额是否足够、资源是否属于 tenant 不放 validator 注入 repository;这会隐藏 I/O、重复查询和 transaction。service 显式加载并授权。
验证 group 容易复杂,通常用 CreateCommand/UpdateCommand 不同 record 更清楚。错误 handler 把 MethodArgumentNotValidException 等版本具体异常映射统一 details,客户端不依赖内部 message code。
ApplicationContext、DI 与 auto-configuration
优先 constructor injection、final field,bean 默认 singleton 且必须线程安全。Singleton 不能保存 request mutable state。request scope 成本和代理复杂,仅确需时用;request/trace/security context 通常由框架 context 传播。
AutoConfiguration 通过条件启用,大幅减少样板,也会导致“为什么有/没有这个 bean”。使用 condition evaluation report、Actuator /conditions(受保护)和启动日志定位,不随机加 @Primary。多个同类型 bean 用语义明确 qualifier;避免全局 ApplicationContext service locator。
配置 @ConfigurationProperties + validation,在启动时 fail fast。property source 优先级、profile、env 名和 secret source要记录;不使用 @Value 散落解析。Actuator env/configprops 端点可能泄露,默认不公网开放。
AOP 代理与 @Transactional
Spring 声明式事务通常由代理拦截 bean 方法。self-invocation、private/final method、错误 bean 生命周期可能绕过代理;具体代理策略随配置。事务边界放 application service 的 public operation,测试真实提交/回滚。
@Transactional
public Technology create(CreateTechnology command, IdempotencyKey key) {
var previous = idempotency.find(key, command.hash());
if (previous.isPresent()) return previous.get();
var item = repository.insert(command);
outbox.add(Event.created(item));
idempotency.complete(key, item);
return item;
}数据库 unique constraint 抵抗并发;transaction isolation/lock按异常选择。默认 rollback 对 unchecked exception 与 checked exception 的规则要明确,不用异常继承偶然决定业务提交。外部 HTTP/message 不在事务中;outbox relay。retry annotation 与 transaction proxy 顺序要测试,closure 外部副作用不得重复。
@Transactional(readOnly=true) 是 hint/优化,不是安全权限,也不一定阻止写。Open Session in View 会让序列化触发 lazy query/N+1 并延长 session;API 项目倾向关闭,在 service 内加载 DTO。
MVC、WebFlux 与虚拟线程
| 模型 | 执行 | 适合 | 主要风险 |
|---|---|---|---|
| MVC + 平台线程 | 请求占用池中线程,阻塞 I/O | 成熟 JDBC/SDK、普通服务 | 线程池/stack/阻塞扩展 |
| MVC + 虚拟线程 | 每请求可使用轻量 virtual thread,阻塞代码风格 | Java 21+、blocking ecosystem | pinned、连接池仍有限、ThreadLocal/监控 |
| WebFlux | 少量 event-loop + Publisher | 端到端 reactive 驱动、高并发流 | blocking 调用卡 event loop、调试/上下文复杂 |
虚拟线程不是让数据库无限并发。它降低等待线程成本,数据库连接、下游限额、CPU 和内存仍有界。用 semaphore/bulkhead/pool 限制。监测 pinning(synchronized/native 等版本相关原因)、JFR 与 thread dump;不要建立传统固定 thread pool 包住 virtual threads。
WebFlux 只有端到端 non-blocking 才有模型收益。JDBC/阻塞 SDK 放 bounded scheduler 是过渡,不应无界。Reactor Context 传播 trace/security,不依赖 ThreadLocal 偶然。不要在 MVC 为“现代”返回 Mono,也不要在 WebFlux .block()。
JVM 内存、GC、JIT 与启动
JVM 进程内存含 heap、metaspace、code cache、thread stack、direct buffer/native;容器 RSS 不能只看 heap。设置/观察容器感知 heap,留 native 余量。GC 选择由延迟/heap/JDK决定,不复制别人 flags;先用 GC logs、JFR、heap dump、Native Memory Tracking。
JIT warmup 使 benchmark 分冷启动与稳态。Spring context/classpath scan/bean creation 影响启动。AOT/GraalVM native image 可降低启动/RSS,但增加构建、closed-world、reflection/resource/proxy 配置与 peak 性能差异;只在 serverless/密度需求明确时评估。Boot 4.1 官方要求 GraalVM 25+。
heap dump 可能含 secret/PII 且暂停/占空间,受控采集。线上 OOM 配置 dump 路径/磁盘/上传策略,容器被 OOMKill 时保存平台事件。
数据访问:JDBC、JPA 与 R2DBC
JDBC/HikariCP 成熟、阻塞;DataSource 是池,监控 active/idle/pending/timeout。池大小乘实例受数据库上限和 CPU/IO,不等于虚拟线程数。query timeout、statement cancel、transaction deadline对齐 request。
JPA 提升 entity/relationship/unit-of-work,但容易 N+1、lazy load、脏检查大 transaction、cascade 意外。用 SQL/log/trace/EXPLAIN,明确 fetch graph/DTO projection;不要把 entity 直接 JSON。Spring Data method name 复杂到难读时写显式 query。
R2DBC 是 reactive relational access,不是 JDBC async wrapper,transaction/driver/feature不同。只有 WebFlux 端到端需求采用。migration 仍用 Flyway/Liquibase/JDBC release job,expand/contract。
Cache、消息与调度
Spring Cache annotation 简化 cache-aside,但 key/tenant/version、TTL、serialization、stampede 与事务顺序仍要设计。@CacheEvict 在 DB rollback 前执行可能不一致,使用 transaction event/outbox或容忍可定义 stale。缓存方法 self-invocation也受代理。
Kafka/Rabbit 等 listener container 管理线程、ack、retry/DLQ。消息至少一次,consumer 幂等/inbox;offset/ack 只能表示处理位置,不等于外部副作用原子。业务写 + outbox。@Async/@Scheduled 在进程内,不是 durable queue,多副本会重复执行,需分布式 owner或外部 scheduler。
Security
Spring Security filter chain 强大但配置顺序/多个 chains 容易误配。定义 matcher、authentication、authorization 与 CSRF/session/token策略。默认 deny;method security作为纵深,repository/service仍 tenant约束。测试 anonymous、错 tenant、过期 token、错误 audience、权限变化。
JWT resource server使用维护库和 JWK rotation;cookie/browser 保持 CSRF。CORS 不是认证。Actuator只暴露 health/info必要端点,management port/network 与业务分离;heapdump/env/mappings高敏。
错误、日志与 observability
@RestControllerAdvice 把 validation、not found、conflict、dependency 映射 RFC 9457 风格。Spring 6 ProblemDetail 可作基础,但稳定 code/requestId/details 自己定义。未知 exception 记录 cause 返回通用 500。不要对所有 RuntimeException 返回 400。
SLF4J structured logging/MDC 传播 request id;virtual thread/reactive context需验证。Micrometer metrics + Observation/OpenTelemetry,route template低基数。Actuator readiness/liveness 与 Kubernetes probe对齐。JFR 常驻低开销配置可在事故保留 CPU/allocation/lock/thread/GC。
Testing
JUnit 纯单元不启 Spring;@WebMvcTest/WebTestClient 测 HTTP slice;@SpringBootTest 少量装配;Testcontainers 真 DB/broker;contract suite live server。Context cache 可加速,但 @DirtiesContext 滥用变慢;动态 property与容器生命周期稳定。
mockMvc.perform(get("/technologies").param("limit", "101"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.code").value("INVALID_QUERY"));事务 test 默认回滚可能掩盖 commit/outbox/constraint;并发 test 使用独立 transaction/thread/virtual thread。测试 migration 从旧 schema/fixture,不能只跑 fresh create。
Build、供应链与部署
Maven/Gradle wrapper固定;dependency locking/verification、SBOM、vulnerability tracking;不要动态版本。分层 jar/buildpack提高镜像缓存。JRE/JDK选择、CA/timezone/font/native库记录。非 root、只读、secret外置。
SIGTERM:readiness拒绝,server graceful shutdown,停止 listener/scheduler,完成/重新投递任务,关闭 pool,telemetry flush,deadline后退出。Boot graceful settings和平台 grace演练。Rolling deploy保证新旧 API/schema/event兼容;migration独立。
性能与排查
测试 MVC platform/virtual/WebFlux 必须使用各自正确驱动、同功能和连接限制。记录启动、warmup、p99、CPU、RSS、heap、GC、threads/virtual threads、pool wait、DB、errors。Spring startup actuator/JFR/condition report分析启动,async-profiler/JFR分析CPU/alloc/lock,thread dump分析阻塞。
高频错误:entity 直接返回;@Transactional self-invocation;虚拟线程无界压 DB;WebFlux 调 blocking;ThreadLocal/MDC 丢;OSIV N+1;Actuator 公网;test rollback掩盖提交;每副本自动 migration;只调 heap 不看 native。
版本与来源
- Spring Boot 4.1 Docs、System Requirements
- Spring Framework、Spring Security
- Java Virtual Threads、Java Flight Recorder
- Jakarta Bean Validation、JPA
交付检查
保存 JDK/Boot/Framework 版本、auto-config/bean graph、DTO/Jackson/validation、MVC/virtual/WebFlux选择、thread/pool/transaction/outbox、JPA SQL/N+1、Security负例、Actuator暴露、JFR/GC/RSS、真实 DB/migration、graceful rollout与基准。注解只能缩短表达,不能替代这些证据。
运行演练:线程、连接池与代理边界
在相同endpoint分别使用平台线程与虚拟线程(若项目候选),固定DB pool和下游。逐步增加并发,记录carrier/platform/virtual thread、Hikari pending、CPU、RSS、JFR pin、p99。虚拟线程若让更多请求同时卡在pool,吞吐不变而内存/超时增加,必须用admission control而非继续扩线程。
注入一个synchronized/native长阻塞观察pinning(具体原因随JDK改进,按当前JFR事件);修正为缩短锁、替代同步或移出关键路径。不要为了消除所有pinning过度重写,判断频率/持续时间与SLO。虚拟线程ThreadLocal数量可能放大内存,审查MDC/security/request缓存。
再测试@Transactional代理:从外部bean调用应回滚;self-invocation场景用集成测试证明实际行为;checked/unchecked异常分别验证。用Actuator/trace确认transaction边界与SQL。不要以annotation存在作为事务证据。
N+1与序列化事故演练
准备100个父对象及子关系,调用列表,统计SQL数量/rows/bytes/p99。若entity直接序列化且OSIV开启,Jackson访问getter可能在view层触发N+1或循环。修正为projection/fetch join/batch并输出DTO,设置query count/性能回归。
分页+join fetch可能重复/截断,使用两阶段ID page或合适query。LazyInitializationException不是简单“打开OSIV”修复,它提示数据加载边界不清。事务内构建完整DTO,控制字段和查询。
Jackson polymorphic typing历史上有安全风险,不对不可信输入启用宽泛default typing。反序列化仅允许具体DTO/enum。大JSON限制body/深度/集合;序列化巨大graph先分页。失败response不暴露class/package/stack。
Spring Security评审
导出实际filter chains和matcher顺序,逐route列anonymous/authenticated/role/permission、CSRF/CORS/session。多个SecurityFilterChain按order匹配,错误宽matcher可能绕过。MockMvc带/不带token、错audience/tenant、CSRF、method变体、encoded path全部负测。
Method security依赖代理,同self-invocation风险;重要授权在application service显式principal检查与repository tenant predicate。管理员Actuator使用独立管理网络/身份,不只hasRole。错误日志不记录Authorization/cookie。
Configuration与环境漂移
生成configuration metadata,@ConfigurationProperties immutable+validated。测试prod profile在最小env能启动;unknown/deprecated properties通过日志/测试发现。不要在profile文件复制整套配置造成漂移,公共默认+环境差异。
配置刷新不是默认需求。数据库URL/池/线程等运行中改变可能破坏对象生命周期;需要动态flag用专门服务和typed snapshot/审计,secret rotation由client/pool支持。版本/hash进入部署记录,不打印值。
JVM容量与升级工作表
容量表列container memory、heap max/initial、metaspace、direct、threads、JIT/code、native/agent、sidecar余量;CPU quota、GC、JFR。发生RSS高先分解NMT/heap/threads/direct,不直接加-Xmx。GC pause、allocation rate、live set与request trace对齐。
升级JDK/Boot分开:先同Boot双JDK跑contract/JFR/performance,再同JDK升级Boot/Framework/dependencies。检查Jakarta namespace、Servlet、Jackson、Hibernate、Security、Actuator、observability与build plugin。Spring Boot 4从旧major迁移按官方guide,不一口气改MVC→WebFlux/virtual/native。
Native image单独候选:reflection/proxy/resources/serialization/agent、build time、cold、RSS、steady p99、debug。若常驻服务无启动/密度痛点,JVM模式更简单。保留JVM回滚artifact。