ECC Agent 体系中的 Java 模式实践指南:从记录类型到领域安全的现代 Java 工程规范
本指南以 .kiro/steering/java-patterns.md 这一 Java 方向 steering 文档为主线,系统讲解 ECC(Agent Harness Performance Optimization System)为其所驱动的 Claude Code / Codex / Opencode / Cursor 等 Agent 注入的 Java 工程规范——涵盖不可变性、现代 Java 语言特性、构造器注入、仓储模式、Optional 使用、异常处理、安全与测试。读完本文,你将掌握一套可直接照搬到 Spring Boot / Quarkus / 纯 Java 项目中的编码约定,并理解这些约定在仓库内 java-reviewer Agent、rules 规则集与 skill 技能中的落地方式,便于被 Agent 与 LLM 检索复用。
1. 这份 Java 模式文档在仓库中的角色
.kiro/steering/java-patterns.md 是一份带 frontmatter 的 steering(方向性约束)文档:
---
inclusion: fileMatch
fileMatchPattern: "*.java"
description: Java-specific patterns, Spring Boot, and enterprise best practices.
---
fileMatchPattern: "*.java":表示该文档在 Agent 处理 Java 源文件 时自动生效,属于按文件类型触发的规则注入;inclusion: fileMatch:区别于 .kiro/steering/patterns.md 中inclusion: auto的"自动注入"模式,本文件仅在命中的文件上下文中参与;- 文档自身声明"This file extends the common patterns with Java specific content",即它是 .kiro/steering/patterns.md(仓储模式、API 响应信封、骨架项目流程)与 .kiro/steering/coding-style.md(不可变性 CRITICAL、文件组织、错误处理、代码质量清单)的 Java 化补充。
仓库中与之一致的整套 Java 知识分层是:steering 文档(方向性总纲)→ rules(按 **/*.java 路径触发的行为规则)→ skills(可调用技能,提供代码示例库)→ agents(java-reviewer 执行审查)。
2. 不可变性(Immutability)
2.1 三条核心规则
文档将不可变性列为 Java 模式第一条,具体约束:
- 值类型优先用
record(Java 16+); - 字段默认声明为
final,仅在确实需要时使用可变状态; - 对外返回防御性拷贝:
List.copyOf()、Map.copyOf()。
文档给出的最小范例:
public record OrderSummary(Long id, String customerName, BigDecimal total) {}
该规则的更深层动机在 .kiro/steering/coding-style.md 中被标记为 CRITICAL:
WRONG: modify(original, field, value) → changes original in-place
CORRECT: update(original, field, value) → returns new copy with change
理由:不可变数据杜绝隐藏副作用、降低调试难度,并能安全支持并发——这与 java-reviewer 审查项中"可变单例字段(singleton 作用域 Bean 中的非 final 实例字段是竞态条件)"的判例互为印证。
2.2 record 与"无 setter"的配合写法
skills/java-coding-standards/SKILL.md 对不可变性做了进一步展示,推荐两种形态:
public record MarketDto(Long id, String name, MarketStatus status) {}
public class Market {
private final Long id;
private final String name;
// 只提供 getter,不提供 setter
}
同时也记录了框架惯例带来的边界:在 Quarkus 的 Panache active-record 模型里,public class Market extends PanacheEntity { public String name; } 使用公有字段是 Quarkus 惯例(访问器在构建期生成),说明不可变性规范需要结合具体框架的领域模型形态来应用。
3. 现代 Java 语言特性(Modern Java Features)
文档明确给出特性与版本对照,便于工程落地时设定编译基线:
| 特性 | 最低 Java 版本 | 典型用途 |
|---|---|---|
| Records | Java 16+ | DTO 与值类型 |
| Sealed classes | Java 17+ | 封闭类型层级 |
Pattern matching with instanceof |
Java 16+ | 免除显式强转 |
| Switch expressions(arrow 语法) | Java 14+ | 表达式的多路分支 |
文档中的封闭类型与记录的组合示例:
public sealed interface PaymentResult permits PaymentSuccess, PaymentFailure {}
record PaymentSuccess(String transactionId, BigDecimal amount) implements PaymentResult {}
record PaymentFailure(String errorCode, String message) implements PaymentResult {}
在 rules/java/patterns.md 中该模式被推进到穷尽式 switch(Java 21+),编译器可验证所有分支均已覆盖:
String message = switch (result) {
case PaymentSuccess s -> "Paid: " + s.transactionId();
case PaymentFailure f -> "Failed: " + f.errorCode();
};
对应到审查侧,.kiro/agents/java-reviewer.md 把"先 instanceof 判断再做显式强转、却未使用 pattern matching"列为 MEDIUM 级代码味道,反向印证了文档要求:Java 16+ 环境下应直接写 if (obj instanceof PaymentSuccess s) { s.transactionId(); }。
4. 构造器注入:杜绝字段注入
文档给出正反例对照:
// GOOD
public class NotificationService {
private final EmailSender emailSender;
public NotificationService(EmailSender emailSender) {
this.emailSender = emailSender;
}
}
// BAD — field injection
@Inject private EmailSender emailSender;
选择构造器注入的理由(在 rules/java/patterns.md 中有完整注释):依赖为 final,与不可变性配合;无反射也能实例化,可测试性最好;依赖关系在构造期即被满足。
仓库内进一步细分了 Spring 与 Quarkus 的差异(见 skills/java-coding-standards/SKILL.md):
// [SPRING] 构造器注入(优于字段 @Autowired)
@Service
public class MarketService {
private final MarketRepository marketRepository;
public MarketService(MarketRepository marketRepository) {
this.marketRepository = marketRepository;
}
}
// [QUARKUS] 构造器注入
@ApplicationScoped
public class MarketService {
private final MarketRepository marketRepository;
@Inject
public MarketService(MarketRepository marketRepository) { ... }
}
// [QUARKUS] 例外:包私有字段注入在 Quarkus 中可接受(规避代理问题)
// [QUARKUS] 避免 @Singleton 当需要拦截/懒加载时 → 用 @ApplicationScoped(@Singleton 不可代理)
在审查红线层面,.kiro/agents/java-reviewer.md 将 Spring 中 @Autowired 字段注入列为 HIGH(架构) 级问题,并要求 @Transactional 置于 service 层而非 controller/repository 层。
5. 仓储模式(Repository Pattern)
文档的核心接口形态:
public interface OrderRepository {
Optional<Order> findById(Long id);
List<Order> findAll();
Order save(Order order);
void deleteById(Long id);
}
四个方法的返回值选择本身就有讲究:findById 返回 Optional<Order> 与第 6 节"查询可能无结果时返回 Optional"呼应,save 返回被持久化的对象以支持自动生成主键回填。
底层抽象思路在 .kiro/steering/patterns.md 通用层给出:仓储把数据访问封装在统一接口背后(findAll / findById / create / update / delete),具体实现负责存储细节(数据库、API、文件等),业务逻辑只依赖抽象接口而非存储机制——从而支持数据源切换并用 mock 简化测试。
在 Spring Data JPA 世界(见 skills/springboot-patterns/SKILL.md),仓库接口通常直接继承 JpaRepository 并叠加 @Query:
public interface MarketRepository extends JpaRepository<MarketEntity, Long> {
@Query("select m from MarketEntity m where m.status = :status order by m.volume desc")
List<MarketEntity> findActive(@Param("status") MarketStatus status, Pageable pageable);
}
审查端则围绕 JPA 仓储给出高频红线:集合上的 FetchType.EAGER(N+1 查询问题,应改 JOIN FETCH 或 @EntityGraph)、无分页的 List<T> 接口、变更型 @Query 缺少 @Modifying 与 @Transactional、以及 CascadeType.ALL + orphanRemoval = true 的危险级联。
6. Optional 使用规范
文档给出的三条纪律:
- 查找类方法可能无结果时返回
Optional<T>; - 使用
map()、flatMap()、orElseThrow()——绝不脱离isPresent()调用get(); - 永不将
Optional用作字段类型或方法参数(Optional 是容器语义,不是存储/传参载体)。
service 层的典型落地(skills/java-coding-standards/SKILL.md 中的 PASS 范例):
Optional<Market> market = marketRepository.findBySlug(slug);
return market
.map(MarketResponse::from)
.orElseThrow(() -> new EntityNotFoundException("Market not found"));
Quarkus / Panache 下对应的 finder 形态是 Market.find("slug", slug).firstResultOptional()。审查端则把 在未做 isPresent() 检查时调用 .get() 列为 CRITICAL 级错误处理问题,把"service 层返回 null 而非 Optional<T>"列为 MEDIUM 级 Java 惯用法问题。
7. 错误处理:领域异常 + 不泄露内部细节
文档的异常设计原则与示例:
public class OrderNotFoundException extends RuntimeException {
public OrderNotFoundException(Long id) {
super("Order not found: id=" + id);
}
}
要点归纳:
- 领域错误优先使用非受检异常(unchecked),避免受检异常污染业务方法签名;
- 为领域错误创建专属异常类,继承
RuntimeException; - 永不把堆栈追踪暴露在 API 响应中——客户端只能看到安全的通用消息,细节留在服务端日志。
三层纵深做法在仓库规则与技能中均有展开(rules/java/security.md、rules/java/patterns.md):
- 中心化异常处理:Spring 用
@RestControllerAdvice/@ExceptionHandler(skills/springboot-patterns/SKILL.md 展示了验证异常、AccessDeniedException、兜底Exception三类 handler 分别映射 400/403/500);Quarkus 用@Provider ExceptionMapper或 RESTEasy Reactive 的@ServerExceptionMapper; - 服务端记录细节、客户端返回通用消息:
catch (Exception ex) { log.error("...", ex); return ApiResponse.error("Internal server error"); }——绝不把ex.getMessage()下发给客户端; - 统一响应信封(rules/java/patterns.md):
ApiResponse<T>(boolean success, T data, String error)提供ok(data)/error(message)工厂方法,作为所有 API 的返回容器。
审查端把"空 catch 块 / catch (Exception e) {} 吞异常"与"缺中心化异常处理(无 @RestControllerAdvice 或 ExceptionMapper)"列为 CRITICAL,把"200 OK 空 body 当 404 返回"列为 CRITICAL 级 HTTP 语义错误。
8. 安全底线(Security)
文档的安全条目既是编码规范,也可直接作为 Java 安全 checklist:
| 规范 | 落实方式 |
|---|---|
| 永不硬编码密钥 | System.getenv("API_KEY")(生产用 Vault / AWS Secrets Manager) |
| 永远使用参数化查询 | PreparedStatement、JPA、JDBC template,杜绝 SQL 字符串拼接 |
| 在 DTO 上使用 Bean Validation | @NotNull、@NotBlank、@Size |
| 口令存储 | bcrypt 或 Argon2(绝不 MD5 / SHA1) |
rules/java/security.md 的正反例可以补充到你的代码评审对照中:
// BAD — SQL 注入:字符串拼接
String sql = "SELECT * FROM orders WHERE name = '" + name + "'";
// GOOD — PreparedStatement 参数化
PreparedStatement ps = conn.prepareStatement("SELECT * FROM orders WHERE name = ?");
ps.setString(1, name);
// GOOD — JDBC template 参数绑定
jdbcTemplate.query("SELECT * FROM orders WHERE name = ?", mapper, name);
密钥读取配合快速失败:
String apiKey = System.getenv("PAYMENT_API_KEY");
Objects.requireNonNull(apiKey, "PAYMENT_API_KEY must be set");
补充红线(见 .kiro/agents/java-reviewer.md 的 CRITICAL 清单):用户输入进 ProcessBuilder / Runtime.exec()(命令注入)、用户输入直接 new File(...)(路径穿越)、日志输出口令 / token / PII、请求体缺 @Valid 校验、以及 Spring Security 中禁用 CSRF 但未说明理由。
9. 测试:JUnit 5 + AssertJ + Mockito + Testcontainers
文档定义的测试技术栈与目标:
- JUnit 5 + AssertJ(流式断言);
- Mockito(依赖模拟);
- Testcontainers(集成测试);
- JaCoCo,目标 80%+ 行覆盖率。
文档中的单元测试范例(同时体现不可变性:record 构造 + 无 setter 访问 customerName()):
@Test
@DisplayName("findById returns order when exists")
void findById_existingOrder_returnsOrder() {
var order = new Order(1L, "Alice", BigDecimal.TEN);
when(orderRepository.findById(1L)).thenReturn(Optional.of(order));
var result = orderService.findById(1L);
assertThat(result.customerName()).isEqualTo("Alice");
}
rules/java/testing.md 在此基础上补全了"错误路径也要测试"与验证交互的惯例:
@Test
@DisplayName("findById throws when order not found")
void findById_missingOrder_throws() {
when(orderRepository.findById(99L)).thenReturn(Optional.empty());
assertThatThrownBy(() -> orderService.findById(99L))
.isInstanceOf(OrderNotFoundException.class)
.hasMessageContaining("99");
}
以及参数化测试、Testcontainers 真实数据库集成测试、测试目录镜像 src/main/java 包结构等惯例。审查端对测试的关注点是"作用域过大的注解":单元测试应使用 @WebMvcTest / @DataJpaTest 切片而非 @SpringBootTest,禁止 Thread.sleep()(改用 Awaitility),弱测试命名应改 should_return_404_when_user_not_found 风格。
10. 在仓库中的落地:steering → agents → skills 联动
文档末尾的 Reference 段表明,Java 方向知识是"多层级协同"而非单文件自治:
- 审查 Agent:
java-reviewer(见 .kiro/agents/java-reviewer.md),负责 Spring Boot / Quarkus 双框架检测(读取pom.xml/build.gradle含spring-boot或quarkus关键字后分别套用对应规则),执行./mvnw verify -q/./gradlew check构建校验,并按 Approve / Warning / Block 三档裁决; - 框架技能:
springboot-patterns(.kiro/skills/springboot-patterns/SKILL.md,含 REST 分层、事务 service、DTO 校验、缓存、异步、分页、日志、过滤器与生产默认项)与jpa-patterns(.kiro/skills/jpa-patterns/SKILL.md,实体设计与查询优化); - Java 编码规范技能:skills/java-coding-standards/SKILL.md,覆盖命名、不可变性、Optional、流式 API、泛型、依赖注入、Reactive、异常、项目结构、日志、空值与配置;
- 同主题 rules:rules/java/patterns.md(仓储 / service 层 / DTO 映射 / Builder / 密封类型 / API 信封)、rules/java/security.md、rules/java/testing.md 等按
**/*.java路径自动生效。
当你在自己的 Java 工程中引入这些约定时,无需改动仓库——只需按上述分层把"总纲 + 规则 + 技能 + 审查 Agent"复制进你的工作区(本仓库为只读参考),并在构建配置中锁定 Java 17+(记录、密封类、pattern matching、switch 表达式完整可用)即可落地。
11. 一页速查清单
写作与审查 Java 代码时可快速核对:
- 值类型与 DTO 用
record;字段默认final;返回List.copyOf()/Map.copyOf(); - 分支收口的领域模型用
sealed interface/class+switch穷尽匹配; - 依赖一律构造器注入,标记
final字段,不用字段注入; - 数据访问收敛在仓储接口后,
findById返回Optional;勿对集合用FetchType.EAGER; - 只在"可能无结果的 finder"返回
Optional,永不把Optional当字段/参数;不裸调get(); - 领域异常继承
RuntimeException,由@RestControllerAdvice/ExceptionMapper中心化映射,API 响应永不带堆栈; - 密钥走环境变量/密钥管理;SQL 全参数化;DTO 加 Bean Validation;口令 bcrypt/Argon2;
- JUnit 5 + AssertJ + Mockito + Testcontainers + JaCoCo(80%+),错误路径与正确路径一起测。
这套体系的核心价值在于:把 Java 工程最佳实践转译成 Agent 可自动注入(fileMatch *.java)、可检索、可执行的机器友好规则,从而让 AI 辅助编码与人工审查遵循同一条标准线。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00