首页 / 资讯中心 / 文章详情

Codex 辅助 Java 开发:Spring Boot 代码生成与单元测试实战

Codex 辅助 Java 开发:Spring Boot 代码生成与单元测试实战 ★ FEATURED ARTICLE
1. 为什么我决定把 Codex 拉进 Java 日常开发流先说结论Codex 不是来抢 Java 工程师饭碗的它更像一个随叫随到、不嫌你问题蠢、还能帮你写单元测试的结对搭档。我所在团队做的是 Spring Boot 微服务日常节奏是需求评审、写 Controller、Service、Mapper、补测试、改 Bug循环往复。真正吃掉时间的往往不是核心业务逻辑而是那些重复度极高的样板代码、边界条件处理、以及让人头大的测试覆盖率补齐。Codex 在这三件事上给我的帮助最直接。这篇内容适合三类人看一是已经会写 Java、但还没把 AI 编程工具真正融进工作流的工程师二是正在搭 Spring Boot 项目、想提升交付速度的开发者三是对代码生成、测试覆盖有实际诉求、又担心生成代码质量不可控的技术负责人。我会把从环境准备、代码生成、测试补齐到踩坑排查的完整链路讲清楚所有步骤都能直接照着复现。需要提前说明的是Codex 本质是一个基于大模型的代码助手它的输出质量高度依赖你给的上下文。你喂给它一个模糊的“帮我写个登录接口”它给你的就是教科书式的玩具代码你给它明确的实体结构、分层约定、异常处理规范它才能产出能进代码库的东西。这个认知差是我用了几个月之后最深的体会也是这篇博文想帮你跨过去的门槛。我下面讲的流程核心围绕 Spring Boot MyBatis-Plus 这套在国内 Java 圈极其常见的组合展开因为这套组合的样板代码最多Codex 的收益也最明显。整个实战链路分成四块整体设计思路、核心细节与实操要点、完整实操过程、以及问题排查。每一块我都会给出可直接抄作业的配置和代码。2. 整体设计与思路拆解2.1 为什么选 Codex 而不是别的生成方式Java 生态里代码生成工具其实不少MyBatis-Plus 自带代码生成器、各种模板引擎、IDEA 插件都能干这事。那为什么还要引入 Codex关键在于生成维度的差异。传统代码生成器是“结构映射型”你给它一张表它按固定模板吐出 Entity、Mapper、Service、Controller字段名、类型、注解都是死的遇到复杂业务逻辑就歇菜。Codex 是“语义理解型”它能读懂你的业务意图生成带条件判断、带异常处理、带日志的代码。举个我实际遇到的场景一个订单状态流转接口涉及待支付、已支付、已发货、已完成、已取消五种状态每种状态允许的操作不同。传统生成器只能给你一个空壳方法Codex 能根据我用自然语言描述的状态机规则直接生成带 switch 分支和状态校验的完整方法体。这就是差异所在。但这里有个前提你必须把项目约定讲清楚。我一般会在对话开头贴一段项目背景包括 JDK 版本、Spring Boot 版本、ORM 框架、统一返回体结构、异常处理方式。这段背景我存成了一个模板每次新会话直接粘贴省得反复解释。2.2 分层约定是生成质量的生命线Codex 生成 Java 代码最容易翻车的地方就是它不知道你的分层边界。比如它可能把业务逻辑写进 Controller或者把数据库查询直接塞进 Service 而不走 Mapper。要解决这个问题你得在提示里明确分层职责。我用的分层约定是这样的Controller 只做参数校验和调用 Service不写业务逻辑Service 承载业务编排和事务控制Mapper 只做数据访问DTO 和 Entity 严格分离DTO 用于对外传输Entity 对应数据库表。这套约定我会在每次让 Codex 生成代码前明确告知实测下来生成结果的可用率能从五成提到八成以上。提示分层约定不要只在脑子里想一定要写进提示词。Codex 不会读心术你省下的那几行说明最后都会变成你改代码的时间。2.3 测试覆盖为什么是 Codex 的最佳战场如果说代码生成还有争议那测试覆盖这块 Codex 的价值几乎是压倒性的。原因很简单单元测试是典型的“高重复、低创造性”工作写起来枯燥但又是质量保障的刚需。很多团队测试覆盖率上不去不是不想写是写起来太耗时间。Codex 生成单元测试有几个天然优势。第一它能根据方法签名自动推断需要 mock 哪些依赖第二它能覆盖正常路径、边界值、异常分支第三它熟悉 JUnit 5、Mockito、AssertJ 这些主流测试框架的写法。我实测过一个 200 行左右的 Service 类手工写全测试大概要一个半小时用 Codex 生成加人工调整二十分钟搞定覆盖率从 40% 提到 85%。当然生成的测试不能无脑信。它有时候会写出“为了覆盖率而覆盖率”的断言比如只断言方法不抛异常这种测试没有实际价值。所以我的做法是Codex 生成骨架我负责补充关键断言和业务语义校验。2.4 整体工作流的四个阶段把上面这些思路串起来我的完整工作流分四个阶段。第一阶段是环境准备把 Codex 接入到 IDE 或命令行配置好项目上下文模板。第二阶段是代码生成按 Entity、Mapper、Service、Controller 的顺序逐层生成每层生成后立即人工审查。第三阶段是测试补齐针对 Service 层核心方法生成单元测试重点覆盖分支逻辑。第四阶段是问题排查处理生成代码编译不过、逻辑偏差、测试失败等常见问题。这个流程不是线性的实际用起来经常来回跳。比如生成 Service 时发现 Entity 字段设计有问题就得回头改 Entity 再重新生成。但整体框架清晰能保证你不迷失在 AI 生成的海量代码里。3. 核心细节解析与实操要点3.1 环境准备把 Codex 接进你的开发流Codex 的接入方式主要有两种IDE 插件和命令行。IDE 插件适合边写边问命令行适合批量生成。我两种都用日常改 Bug 用插件批量生成用命令行。环境准备的第一步是确认 JDK 版本。我建议用 JDK 17这是目前 Spring Boot 3.x 的基线版本也是长期支持版本。如果你还在用 JDK 8Codex 生成的代码可能会用到一些新语法比如 record、switch 表达式导致编译不过。这种情况下你要在提示里明确说明 JDK 版本让它避开新特性。第二步是准备项目上下文模板。我用的模板长这样项目背景 - JDK 17Spring Boot 3.2.x - ORM 使用 MyBatis-Plus 3.5.x - 统一返回体 ResultT包含 code、message、data 三个字段 - 全局异常处理使用 RestControllerAdvice - 分层约定Controller 只做参数校验和调用 ServiceService 承载业务逻辑和事务Mapper 只做数据访问 - DTO 与 Entity 严格分离 - 日志使用 SLF4J禁止使用 System.out这段模板每次新会话粘贴一次能省掉大量来回解释。实测下来带模板的生成结果和裸问的生成结果质量差距非常明显。第三步是配置代码风格。Codex 默认生成的代码缩进、命名风格可能和你的项目不一致。我一般会在提示里加一句“遵循阿里巴巴 Java 开发手册命名规范”这样生成的变量名、方法名基本能对齐团队习惯。3.2 代码生成从 Entity 到 Controller 的逐层推进代码生成我坚持逐层推进不一次性让 Codex 生成整个模块。原因是一次性生成的内容太多审查成本高而且一旦底层设计有问题上层全得返工。Entity 层生成。我会把数据库表结构贴给 Codex让它生成对应的 Entity 类。这里有个细节MyBatis-Plus 的注解要写全包括 TableName、TableId、TableField。特别是逻辑删除字段和自动填充字段一定要在提示里说明否则 Codex 不会主动加。Data TableName(t_order) public class Order { TableId(type IdType.AUTO) private Long id; private String orderNo; private Long userId; private BigDecimal amount; TableField(fill FieldFill.INSERT) private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; TableLogic private Integer deleted; }Mapper 层生成。Mapper 接口本身很简单继承 BaseMapper 就行。但如果你有复杂查询可以让 Codex 生成对应的 XML 或注解 SQL。我一般用注解 SQL 处理简单查询复杂查询才写 XML。Service 层生成。这是 Codex 价值最大的地方。我会把业务规则用自然语言描述清楚让它生成 Service 实现。比如订单状态流转我会写“待支付状态只能流转到已支付或已取消已支付只能流转到已发货其他状态不允许流转”Codex 就能生成带状态校验的方法。Controller 层生成。Controller 相对模板化重点是参数校验注解要写全。我会在提示里明确“使用 Valid 做参数校验校验失败由全局异常处理器统一处理”。3.3 测试覆盖让 Codex 补齐分支逻辑测试生成的关键是让 Codex 理解“要测什么”。我一般会告诉它针对这个 Service 方法生成正常路径、参数为空、业务规则不满足、依赖抛异常四类测试用例。ExtendWith(MockitoExtension.class) class OrderServiceTest { Mock private OrderMapper orderMapper; InjectMocks private OrderServiceImpl orderService; Test void should_throw_exception_when_order_not_found() { when(orderMapper.selectById(1L)).thenReturn(null); assertThrows(BizException.class, () - orderService.pay(1L)); } Test void should_update_status_when_pay_success() { Order order new Order(); order.setId(1L); order.setStatus(OrderStatus.PENDING_PAY.getCode()); when(orderMapper.selectById(1L)).thenReturn(order); orderService.pay(1L); assertEquals(OrderStatus.PAID.getCode(), order.getStatus()); verify(orderMapper).updateById(order); } }这里有个经验Codex 生成的测试经常忘记 verify 关键交互比如上面这个例子如果不 verify updateById 被调用测试就失去了验证数据是否落库的意义。所以生成后一定要检查 verify 语句。3.4 参数计算与选型背后的逻辑有人会问为什么不用 MyBatis-Plus 自带的代码生成器非要绕一圈用 Codex我的答案是两者不冲突场景不同。表结构固定、字段简单的 CRUD用自带生成器更快涉及业务逻辑、状态流转、复杂校验的用 Codex 更合适。还有一个选型点是测试框架。JUnit 5 Mockito 是目前主流Codex 对这套组合的支持最好。如果你还在用 JUnit 4生成的代码可能需要手动调整注解。我建议直接上 JUnit 5迁移成本不高收益明显。4. 实操过程与核心环节实现4.1 从零搭建一个 Spring Boot 模块我拿一个真实的用户管理模块举例完整走一遍流程。这个模块包含用户注册、查询、状态变更三个功能。第一步建表。表结构如下CREATE TABLE t_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(64) NOT NULL, email VARCHAR(128), status TINYINT DEFAULT 1, create_time DATETIME, update_time DATETIME, deleted TINYINT DEFAULT 0 );第二步让 Codex 生成 Entity。我把表结构和项目上下文模板一起贴给它提示词是“根据以下表结构生成 MyBatis-Plus Entity 类遵循项目分层约定”。生成的 User 类基本可用我只调整了字段注释。第三步生成 Mapper。提示词是“生成 UserMapper 接口继承 BaseMapper并添加根据用户名查询的方法”。Codex 生成了接口和对应的注解 SQL我检查后直接采用。第四步生成 Service。这是重点。我的提示词是“生成 UserService 和 UserServiceImpl包含注册、根据 ID 查询、禁用用户三个方法。注册时校验用户名唯一禁用时校验用户存在且当前状态为启用”。Codex 生成的代码如下Service RequiredArgsConstructor public class UserServiceImpl implements UserService { private final UserMapper userMapper; Override Transactional(rollbackFor Exception.class) public Long register(UserRegisterDTO dto) { Long count userMapper.selectCount( new LambdaQueryWrapperUser().eq(User::getUsername, dto.getUsername())); if (count 0) { throw new BizException(用户名已存在); } User user new User(); user.setUsername(dto.getUsername()); user.setEmail(dto.getEmail()); user.setStatus(UserStatus.ENABLED.getCode()); userMapper.insert(user); return user.getId(); } Override public User getById(Long id) { User user userMapper.selectById(id); if (user null) { throw new BizException(用户不存在); } return user; } Override Transactional(rollbackFor Exception.class) public void disable(Long id) { User user userMapper.selectById(id); if (user null) { throw new BizException(用户不存在); } if (!UserStatus.ENABLED.getCode().equals(user.getStatus())) { throw new BizException(当前状态不允许禁用); } user.setStatus(UserStatus.DISABLED.getCode()); userMapper.updateById(user); } }这段代码我几乎没改就用了。唯一调整的是把异常信息抽成了常量方便后续国际化。第五步生成 Controller。提示词是“生成 UserController提供注册、查询、禁用三个接口使用 Valid 校验参数返回统一 Result 结构”。生成的 Controller 结构清晰参数校验注解齐全。4.2 测试覆盖的完整实操Service 写完后我让 Codex 生成单元测试。提示词是“为 UserServiceImpl 生成 JUnit 5 Mockito 单元测试覆盖正常路径、参数异常、业务规则不满足、依赖返回空四类场景”。生成的测试类包含六个测试方法覆盖了注册成功、注册用户名重复、查询成功、查询不存在、禁用成功、禁用状态不合法。我逐个检查断言发现两个问题一是注册成功的测试没有 verify insert 被调用二是禁用成功的测试没有断言状态字段被更新。我手动补上后测试才算完整。跑一遍测试覆盖率报告显示 UserServiceImpl 行覆盖率 92%分支覆盖率 88%。这个数字对于业务代码来说已经相当不错了。4.3 参数选择与配置细节在实操过程中有几个参数和配置需要特别注意。第一是事务注解的 rollbackFor一定要显式指定 Exception.class否则默认只回滚 RuntimeException受检异常不会回滚。第二是 MyBatis-Plus 的逻辑删除配置要在 application.yml 里配好 logic-delete-field 和 logic-delete-value。第三是统一返回体的 code 规范建议用枚举管理不要散落魔法数字。mybatis-plus: global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0这些配置我会在项目初始化时就定好然后写进 Codex 的上下文模板这样生成的代码天然符合规范。4.4 生成代码的审查清单Codex 生成的代码不能直接提交我有一套固定的审查清单。第一看分层是否越界Controller 里有没有业务逻辑。第二看异常处理是否统一有没有直接抛 RuntimeException。第三看事务边界是否正确该加 Transactional 的方法有没有加。第四看空指针风险集合和对象有没有判空。第五看日志是否合理关键路径有没有打日志。这套清单走一遍基本能拦住八成以上的问题。剩下的两成靠单元测试兜底。5. 常见问题与排查技巧实录5.1 生成代码编译不过怎么办最常见的问题是 Codex 用了项目里不存在的类或方法。比如它可能生成一个Result.success()调用但你的项目里统一返回体叫R.ok()。这种问题的根源是上下文没给全。解决办法是在提示里明确写出统一返回体的类名和方法名或者直接把相关类的代码贴给它。还有一种情况是 JDK 版本不匹配。Codex 可能生成 record 或 switch 表达式而你的项目是 JDK 8。这时候要么升级 JDK要么在提示里明确“使用 JDK 8 语法不要使用 record 和 switch 表达式”。5.2 生成的测试跑不过怎么排查测试跑不过通常有三类原因。第一类是 mock 没配好比如 when 语句的参数和实际调用不一致。第二类是断言写错了比如期望值和实际值类型不匹配。第三类是测试之间有状态污染比如共享了静态变量。我的排查顺序是先看报错信息定位到具体行再看 mock 配置最后看测试隔离。大部分问题在前两步就能解决。5.3 生成代码逻辑偏差怎么处理Codex 有时候会“自作主张”加一些你没要求的逻辑比如自动加缓存、自动加限流。这些逻辑本身可能没错但不符合你的设计意图。处理办法是在提示里明确“只生成我要求的功能不要添加额外逻辑”。如果已经生成了直接删掉多余部分即可。还有一种偏差是业务规则理解错误。比如你说“禁用用户”它可能理解成“删除用户”。这种问题只能靠人工审查发现所以审查环节不能省。5.4 常见问题速查表问题现象可能原因解决思路编译报错找不到类上下文缺失Codex 用了不存在的类补充项目上下文明确类名和方法名语法不兼容JDK 版本不匹配提示中明确 JDK 版本和语法限制测试 mock 失败when 参数与实际调用不一致检查参数类型和值使用 any() 匹配断言失败期望值与实际值不符打印实际值核对业务逻辑事务不回滚未指定 rollbackFor显式添加 rollbackFor Exception.class逻辑删除失效配置缺失检查 application.yml 逻辑删除配置生成多余逻辑提示不够明确明确“只生成要求的功能”命名风格不一致未指定规范提示中要求遵循阿里巴巴 Java 开发手册5.5 独家避坑经验用了几个月我攒了几条文档里不会写的经验。第一条不要在同一个会话里让 Codex 生成太多东西。会话越长上下文越混乱生成质量越差。我一般每生成一个模块就开新会话重新贴上下文模板。第二条生成的代码一定要跑一遍再提交。Codex 生成的代码看起来再合理也可能有隐藏的编译错误或逻辑问题。我见过最离谱的一次是它生成了一个死循环表面上看逻辑完全正常。第三条测试覆盖率不要盲目追求 100%。有些 getter、setter、简单委托方法没必要写测试强行覆盖只会增加维护成本。我的标准是核心业务逻辑覆盖率 85% 以上工具类和方法覆盖率 70% 以上。第四条把 Codex 当助手而不是替代品。它擅长的是重复劳动和样板代码不擅长的是架构设计和复杂业务建模。把精力放在它不擅长的地方才是正确的用法。5.6 性能与效率的实测数据我记录了一组实测数据供你参考。一个标准的 CRUD 模块包含 Entity、Mapper、Service、Controller 四层手工编写大约需要 2 小时用 Codex 生成加审查大约 40 分钟效率提升约 3 倍。单元测试部分手工编写覆盖率 80% 大约需要 1.5 小时Codex 生成加调整大约 25 分钟效率提升约 3.5 倍。但要注意这个数据的前提是你已经把项目上下文模板准备好并且熟悉审查流程。第一次用的时候因为要反复调整提示词效率可能还不如手工写。用熟了之后收益才逐渐显现。6. 把 Codex 用成真正的生产力工具写到这里我想分享一个我自己的体会。Codex 这类工具最大的价值不是帮你省下多少敲键盘的时间而是帮你把注意力从重复劳动里解放出来放到真正需要思考的地方。以前我写一个模块一半时间在写样板代码一半时间在想业务逻辑。现在样板代码交给 Codex我几乎全部精力都能放在业务建模和边界条件上。但这个转变有个前提你得愿意花时间调教它。提示词怎么写、上下文怎么给、生成结果怎么审这些都是需要练习的技能。我见过不少人用了一次觉得生成质量不行就放弃了其实问题不在工具在于还没找到和它协作的节奏。最后再分享一个小技巧我会把每次生成效果特别好的提示词存下来按场景分类比如“Entity 生成”“Service 业务逻辑生成”“单元测试生成”。下次遇到类似场景直接复用省去重新组织语言的时间。这个提示词库用久了就是你个人的效率资产。如果你也在用 Codex 做 Java 开发欢迎交流你的提示词技巧和踩坑经验。这个领域变化很快互相学习比单打独斗走得远。
阅读完成 · 觉得有帮助?
咨询建站