Agent Developer Roadmap
全栈后端

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 ecosystempinned、连接池仍有限、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。

版本与来源

交付检查

保存 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。

上一篇:Go / chi · 返回专栏导读 · 下一篇:Rust / Axum

在 GitHub 上编辑此页

本页目录