Java/Spring Boot 代码审查规范
版本: 8.0 | 模式: Code Review | 检查项: 25 | 更新: 2026-01-28
本规范基于 java-spring-guidelines v5.0 编写
使用方式
用户在代码编写完成后,输入以下命令触发审查:
/java-review
或自然语言:
- •"对照规范检查一下"
- •"review 刚才写的代码"
- •"帮我审查这些修改"
- •"java review"
- •"检查代码质量"
审查流程
第一步:识别审查范围
确定需要审查的代码:
- •本次会话新增/修改的文件
- •用户指定的文件或目录
- •最近 git 变更的文件
- •用户粘贴的代码片段
第二步:逐项检查
按以下检查清单逐项审查,输出格式:
## 审查结果 ### ✅ 通过项 - [检查项]: 通过 ### ❌ 问题项 - [检查项]: 问题描述 - 位置: `文件路径:行号` - 问题: 具体问题 - 修复: 建议的修改
第三步:提供修复
对于问题项,直接提供修复代码或询问用户是否自动修复。
检查清单
1. 命名规范检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| Controller 类 | 必须以 Controller 结尾 | ⚠️ 中 |
| Service 接口 | 必须以 I*Service 命名 | ⚠️ 中 |
| Service 实现 | 必须以 *ServiceImpl 结尾 | ⚠️ 中 |
| Mapper 接口 | 必须以 *Mapper 结尾 | ⚠️ 中 |
| 实体类 | 驼峰命名,与表名对应 | ⚠️ 中 |
| DTO 类 | 必须以 *DTO 结尾 | ⚠️ 中 |
| 请求对象 | 必须以 *Req 结尾 | ⚠️ 中 |
| 响应对象 | 必须以 *Rsp 结尾 | ⚠️ 中 |
| 枚举类 | 必须以 *Enum 结尾 | ⚠️ 中 |
| 方法/字段 | lowerCamelCase | ⚠️ 中 |
| 常量 | UPPER_SNAKE_CASE | ⚠️ 中 |
| 接口路径 | kebab-case(如 /product-catalog/page) | ⚠️ 中 |
检查示例:
// ❌ 问题
class ProductCtrl { }
class ProductImpl { }
interface IProduct { }
// ✅ 正确
class ProductController { }
class ProductServiceImpl implements IProductService { }
interface IProductService { }
2. Import 规范检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| 全限定类名 | 禁止使用(如 java.util.List),必须先 import | ⚠️ 中 |
| 静态导入 | 常量和静态方法使用 import static | ⚡ 低 |
| 通配符导入 | 禁止 import xxx.* | ⚠️ 中 |
检查示例:
// ❌ 问题
public java.util.List<String> getList() {
return new java.util.ArrayList<>();
}
import com.dsl.service.*;
// ✅ 正确
import java.util.List;
import java.util.ArrayList;
import static com.dsl.base.exception.util.ServiceExceptionUtil.exception;
3. 类结构检查
| 检查项 | 规则 | 严重度 | 适用范围 |
|---|---|---|---|
| Controller 类注解 | 建议有 @Validated(视业务需求) | ⚡ 低 | 新建类 |
| Service 类注解 | 必须有 @Slf4j + @RequiredArgsConstructor | ⚠️ 中 | 新建类 |
| 依赖注入方式 | 新建类必须构造器注入,存量类不强制修改 | ⚠️ 中 | 新建类 |
| 类命名 | Controller/Service/Mapper/DTO 等后缀正确 | ⚠️ 中 | 所有 |
注意:存量代码中的 @Autowired 字段注入无需修改,只对新建类做要求。
检查示例:
// ✅ 新建类应该这样写
@Service
@Slf4j
@RequiredArgsConstructor
public class ProductServiceImpl implements IProductService {
private final ProductMapper productMapper;
}
// ⚠️ 存量代码(不强制修改)
@Service
public class ProductService {
@Autowired
private ProductMapper productMapper;
}
4. Controller 层检查
| 检查项 | 规则 | 严重度 | 说明 |
|---|---|---|---|
| 职责边界 | 禁止在 Controller 写业务逻辑 | 🔴 高 | 只接收参数、调用 Service、返回结果 |
| 返回类型 | 必须是 CommonResult<T>,禁止返回 Entity | 🔴 高 | 强制 |
| 直接注入 Mapper | 禁止在 Controller 直接注入 Mapper | 🔴 高 | 必须通过 Service |
| 参数校验 | 建议有 @Valid @RequestBody(视业务需求) | ⚡ 低 | 建议 |
| 嵌套校验 | 嵌套对象必须加 @Valid 触发内部校验 | 🔴 高 | 强制 |
检查示例:
// ❌ 问题:Controller 包含业务逻辑
@PostMapping("/add")
public CommonResult<Long> add(@RequestBody ProductAddReq req) {
Product product = new Product();
BeanUtils.copyProperties(req, product);
productMapper.insert(product); // ❌ 业务逻辑
return CommonResult.success(product.getId());
}
// ✅ 正确
@PostMapping("/add")
public CommonResult<Long> add(@Valid @RequestBody ProductAddReq req) {
return CommonResult.success(productService.add(req));
}
5. Service 层检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| 方法行数 | 不超过 50 行 | ⚠️ 中 |
| if-else 嵌套 | 不超过 2 层 | ⚠️ 中 |
| 分支数量 | 超过 3 个考虑策略模式 | ⚡ 低 |
| 卫语句 | 使用卫语句减少嵌套 | ⚠️ 中 |
检查示例:
// ❌ 问题:嵌套过深
if (order != null) {
if (order.getStatus() == 1) {
if (order.getAmount() > 0) {
process(order);
}
}
}
// ✅ 正确:卫语句
if (order == null) {
throw exception(ORDER_NOT_EXISTS);
}
if (order.getStatus() != 1) {
throw exception(INVALID_STATUS);
}
process(order);
6. DTO/Req/Rsp 类检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| Lombok 注解 | 必须有 @NoArgsConstructor(MyBatis 映射需要) | 🔴 高 |
| 字段校验 | Req 类必须有校验注解 | ⚠️ 中 |
| 嵌套对象校验 | 嵌套对象必须加 @Valid 触发内部校验 | 🔴 高 |
| 继承字段 | 子类必须显式声明所有需映射的字段 | 🔴 高 |
嵌套对象校验示例:
// ❌ 问题:嵌套对象未加 @Valid
@Data
public class OrderReq {
@NotNull(message = "用户信息不能为空")
private UserInfo userInfo; // 内部校验不会触发
}
// ✅ 正确:嵌套对象必须加 @Valid
@Data
public class OrderReq {
@NotNull(message = "用户信息不能为空")
@Valid // 必须加
private UserInfo userInfo;
}
7. 日志规范检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| 日志格式 | 必须使用 [业务名称] 前缀 + {} 占位符 | ⚠️ 中 |
| 字符串拼接 | 禁止 log.info("ID: " + id) | ⚠️ 中 |
| 敏感信息 | phone/idCard/password/token 等必须脱敏 | 🔴 高 |
| 异常日志 | catch 块必须 log.error + 异常堆栈 | 🔴 高 |
| 业务方法入口 | 必须打印关键参数 | ⚠️ 中 |
| 外部系统调用 | 调用前后必须打印日志 | ⚠️ 中 |
敏感字段识别模式:
- •
*phone*,*mobile*,*tel*→ 必须脱敏 - •
*idCard*,*idNo*→ 必须脱敏 - •
*password*,*pwd*,*secret*→ 禁止打印 - •
*token*,*apiKey*→ 禁止打印
检查示例:
// ❌ 问题
log.info("用户手机号: " + phone);
// ✅ 正确
log.info("[用户注册],手机号: {}", DesensitizeUtil.mobile(phone));
8. 异常处理检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| 业务异常 | 必须使用 ServiceExceptionUtil.exception() | 🔴 高 |
| 异常信息 | 禁止直接 throw new RuntimeException() | 🔴 高 |
| catch 块 | 禁止空 catch,必须处理或重抛 | 🔴 高 |
| 异常日志 | catch 中必须 log.error 并包含异常堆栈 | 🔴 高 |
检查示例:
// ❌ 问题:原生异常
if (user == null) {
throw new RuntimeException("用户不存在");
}
// ✅ 正确:使用 ServiceExceptionUtil
import static com.dsl.base.exception.util.ServiceExceptionUtil.exception;
if (user == null) {
throw exception(USER_NOT_FOUND);
}
9. 事务检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| 多表写操作 | 必须有 @Transactional(rollbackFor = Exception.class) | 🔴 高 |
| 事务方法修饰符 | 必须是 public | 🔴 高 |
| 多数据源冲突 | 禁止事务方法中混用多个数据源 | 🔴 高 |
| 同类调用 | 禁止同类内部调用事务方法(代理失效) | 🔴 高 |
| 异常捕获 | catch 块不能吞掉异常 | 🔴 高 |
多数据源事务限制:
@Transactional 只对主数据源生效,事务方法中 ❌ 禁止混用 MySQL 和 Doris。
// ❌ 问题:事务中混用 MySQL 和 Doris
@Transactional(rollbackFor = Exception.class)
public void syncData() {
List<Data> dorisData = dorisMapper.selectList();
mysqlMapper.saveBatch(dorisData);
}
// ✅ 正确:拆分方法
public void syncData() {
List<Data> dorisData = queryFromDoris();
saveToMysql(dorisData);
}
@Transactional(rollbackFor = Exception.class)
public void saveToMysql(List<Data> data) {
mysqlMapper.saveBatch(data);
}
10. Mapper 层检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| 复杂查询方式 | 多表JOIN/动态条件≥3个 必须用 XML | ⚠️ 中 |
| SQL 注入 | 禁止 ${} 拼接,必须用 #{} | 🔴 高 |
| XML 与 DTO 同步 | 新增字段必须同步更新 DTO | 🔴 高 |
| 动态排序 | 使用 <choose> 白名单,禁止直接拼接字段名 | 🔴 高 |
动态排序安全写法:
<!-- ✅ 正确:白名单方式 -->
ORDER BY
<choose>
<when test="orderColumn == 'create_time'">create_time</when>
<when test="orderColumn == 'update_time'">update_time</when>
<otherwise>id</otherwise>
</choose>
11. 性能检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| N+1 查询 | 禁止循环查询数据库,必须批量查询 + 内存关联 | 🔴 高 |
| 深度分页 | 大偏移量使用游标分页(WHERE id > lastId) | ⚠️ 中 |
| 批量处理 | 超过 1000 条必须分批 | ⚠️ 中 |
| 大数据量导出 | 使用流式查询 @Options(fetchSize = 1000) | ⚠️ 中 |
N+1 查询示例:
// ❌ 问题:循环查询
for (Order order : orders) {
User user = userMapper.selectById(order.getUserId()); // N 次查询
}
// ✅ 正确:批量查询 + 内存关联
Set<Long> userIds = orders.stream().map(Order::getUserId).collect(Collectors.toSet());
Map<Long, User> userMap = userMapper.selectBatchIds(userIds).stream()
.collect(Collectors.toMap(User::getId, u -> u));
12. 缓存检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| Key 命名 | 必须 {业务}:{模块}:{标识} 格式 | ⚠️ 中 |
| TTL 设置 | 禁止永不过期 | ⚠️ 中 |
| 缓存穿透 | 空值也缓存(短 TTL 如 5 分钟) | ⚠️ 中 |
| 缓存更新 | 先更新数据库,再删除缓存 | ⚠️ 中 |
TTL 参考值:
| 数据类型 | 建议 TTL |
|---|---|
| 热点数据 | 1-5 分钟 |
| 普通数据 | 30 分钟 |
| 配置数据 | 1 小时 |
13. 并发控制检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| 幂等性 | 写接口必须考虑重复调用 | ⚠️ 中 |
| 乐观锁 | 更新操作需考虑并发,优先使用 version 字段 | ⚠️ 中 |
| 分布式锁 | 跨实例操作必须使用分布式锁 | 🔴 高 |
| 锁粒度 | 锁 Key 必须精确到业务主键 | ⚠️ 中 |
| 锁释放 | 只释放自己持有的锁 lock.isHeldByCurrentThread() | 🔴 高 |
14. 安全规范检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| SQL 注入 | MyBatis 禁止 ${} 拼接用户输入 | 🔴 高 |
| XSS 过滤 | 用户输入必须过滤或转义 | 🔴 高 |
| 文件上传 | 类型白名单 + 大小限制 + UUID重命名 | 🔴 高 |
| 数据权限 | 查询/修改必须校验数据归属 | 🔴 高 |
| 配置安全 | 数据库密码、API Key 等禁止明文提交 | 🔴 高 |
数据权限校验示例:
// ❌ 典型漏洞:水平越权
@GetMapping("/orders/{id}")
public CommonResult<OrderDetailRsp> getOrder(@PathVariable Long id) {
return CommonResult.success(orderService.getById(id)); // 未校验归属
}
// ✅ 正确:校验数据归属
@GetMapping("/orders/{id}")
public CommonResult<OrderDetailRsp> getOrder(@PathVariable Long id) {
Order order = orderService.getById(id);
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
if (!order.getUserId().equals(SecurityUtils.getUserId())) {
throw exception(NO_PERMISSION);
}
return CommonResult.success(convert(order));
}
15. 异步处理检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| 线程池指定 | 必须使用 @Async("线程池名") 指定线程池 | 🔴 高 |
| 默认线程池 | 禁止使用默认 SimpleAsyncTaskExecutor | 🔴 高 |
| 同类调用 | 禁止在同类中调用 @Async 方法(代理失效) | 🔴 高 |
| 事务冲突 | 禁止在 @Async 方法中使用 @Transactional | 🔴 高 |
16. 微服务调用检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| Feign 超时 | 必须显式配置超时时间 | 🔴 高 |
| Feign 异常 | 必须捕获处理 FeignException | 🔴 高 |
| 服务调用日志 | 调用前后必须打印日志 | ⚠️ 中 |
Feign 异常处理示例:
// ✅ 正确:捕获并转换为业务异常
public UserDTO getUser(Long userId) {
try {
log.info("[用户查询],调用用户服务,userId: {}", userId);
UserDTO user = userClient.getById(userId);
log.info("[用户查询],调用成功,userId: {}", userId);
return user;
} catch (FeignException.NotFound e) {
log.warn("[用户查询],用户不存在,userId: {}", userId);
return null;
} catch (FeignException e) {
log.error("[用户查询],调用失败,userId: {},status: {},异常:",
userId, e.status(), e);
throw exception(USER_SERVICE_ERROR);
}
}
17. MQ 消费检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| 消费幂等 | 必须保证幂等消费(Redis 或数据库去重) | 🔴 高 |
| 消息确认 | 业务成功后再 ACK | 🔴 高 |
| 死信处理 | 配置死信队列 + 告警 | ⚠️ 中 |
| 事务消息 | 分布式事务使用 RocketMQ 事务消息 | 🔴 高 |
18. 数据库设计检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| 基础字段 | 必须包含 id, create_time, update_time, creator, updater, deleted | ⚠️ 中 |
| 字段类型 | 金额使用 decimal,禁止 float/double | 🔴 高 |
| 字段命名 | 使用 snake_case,全小写 | ⚠️ 中 |
| 索引命名 | 唯一索引 uk_,普通索引 idx_ | ⚠️ 中 |
| 字符集 | 使用 utf8mb4 | ⚠️ 中 |
19. 接口文档检查(Apifox)
| 检查项 | 规则 | 严重度 |
|---|---|---|
| Controller 类注释 | 必须有类级别 Javadoc 描述模块功能 | ⚠️ 中 |
| 方法注释 | 必须有 @param 和 @return 说明 | ⚠️ 中 |
| 字段注释 | DTO/Req/Rsp 字段必须有 Javadoc 注释 | ⚠️ 中 |
| Mock 值 | 使用 @mock 标签提供示例值 | ⚡ 低 |
20. 代码风格检查
| 检查项 | 规则 | 严重度 |
|---|---|---|
| 集合判空 | 使用 CollectionUtils.isEmpty() | ⚡ 低 |
| 字符串判空 | 使用 StringUtils.isBlank() | ⚡ 低 |
| Optional 使用 | 链式调用防空 | ⚡ 低 |
| 对象比较 | 使用 Objects.equals() | ⚡ 低 |
输出模板
审查完成后,按以下格式输出:
报告头部
## Java/Spring 代码审查报告 > 📋 审查范围: X 个文件 | 🕐 YYYY-MM-DD HH:mm ### 问题汇总 | 严重度 | 数量 | 可自动修复 | |--------|------|-----------| | 🔴 高 | X | X | | ⚠️ 中 | X | X | | ⚡ 低 | X | X |
问题详情(diff 格式)
每个问题使用编号和 diff 格式:
### 🔴 高严重度问题 #### #1 [问题标题] 📍 `文件路径:行号` ```diff - // 原代码(红色删除) + // 修复代码(绿色新增)
🔧 可自动修复 - 回复 fix #1 应用修复
#2 [问题标题]
📍 文件路径:行号
// 上下文代码 - // 问题代码 + // 修复代码
⚠️ 需手动修复 - [原因说明]
### 通过项汇总(折叠显示) ```markdown ### ✅ 审查通过 **X/20 项检查通过**(命名规范、Import规范、Mapper层、异常处理...)
后续操作提示
### 📌 后续操作 | 命令 | 说明 | |------|------| | `fix all` | 自动修复所有可修复问题 | | `fix #1` | 修复指定问题 | | `fix #1,#3,#5` | 批量修复多个问题 | | `详细 #2` | 查看问题详细说明 |
快速参考
必须的注解组合
// Controller(新建类) @RestController @RequiredArgsConstructor // @Validated 视业务需求,不强制 // Service(新建类) @Service @Slf4j @RequiredArgsConstructor // DTO @Data @NoArgsConstructor
日志格式
log.info("[业务名称],动作描述,参数: {}", value);
log.error("[业务名称],错误描述,参数: {},异常:", value, e);
敏感字段脱敏
DesensitizeUtil.mobile(phone) // 138****1234 DesensitizeUtil.idCard(idCard) // 310***********1234 // password/token 禁止打印
事务注解
@Transactional(rollbackFor = Exception.class)
异常抛出
import static com.dsl.base.exception.util.ServiceExceptionUtil.exception; throw exception(ERROR_CODE_CONSTANT);
缓存 Key 格式
String key = "{业务}:{模块}:{id}";
// 示例: "order:detail:12345"
分布式锁模板
RLock lock = redisson.getLock("业务:操作:" + bizId);
try {
if (lock.tryLock(3, 30, TimeUnit.SECONDS)) {
// 业务逻辑
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
审查报告示例
## Java/Spring 代码审查报告
> 📋 审查范围: 3 个文件 | 🕐 2026-01-28 14:30
### 问题汇总
| 严重度 | 数量 | 可自动修复 |
|--------|------|-----------|
| 🔴 高 | 2 | 1 |
| ⚠️ 中 | 3 | 2 |
| ⚡ 低 | 1 | 0 |
---
### 🔴 高严重度问题
#### #1 Controller 包含业务逻辑
📍 `ProductController.java:45`
```diff
- @PostMapping("/add")
- public CommonResult<Long> add(@RequestBody ProductAddReq req) {
- Product product = new Product();
- BeanUtils.copyProperties(req, product);
- productMapper.insert(product);
- return CommonResult.success(product.getId());
- }
+ @PostMapping("/add")
+ public CommonResult<Long> add(@Valid @RequestBody ProductAddReq req) {
+ return CommonResult.success(productService.add(req));
+ }
🔧 可自动修复 - 回复 fix #1 应用修复
#2 事务方法混用多数据源
📍 DataSyncServiceImpl.java:78
@Transactional(rollbackFor = Exception.class)
public void syncData() {
- List<Data> dorisData = dorisMapper.selectList();
- mysqlMapper.saveBatch(dorisData);
+ List<Data> dorisData = queryFromDoris();
+ saveToMysql(dorisData);
}
⚠️ 需手动修复 - 涉及方法拆分,需人工确认
⚠️ 中严重度问题
#3 日志缺少业务标识
📍 OrderServiceImpl.java:23
- log.info("订单创建成功,orderId: {}", orderId);
+ log.info("[订单创建],创建成功,orderId: {}", orderId);
🔧 可自动修复
#4 嵌套对象缺少 @Valid
📍 OrderReq.java:15
@NotNull(message = "用户信息不能为空") + @Valid private UserInfo userInfo;
🔧 可自动修复
#5 方法超过50行
📍 ReportServiceImpl.java:120-185
⚠️ 需手动修复 - 建议拆分为多个私有方法
⚡ 低严重度问题
#6 缺少 @mock 注释
📍 ProductReq.java:12
+ /** + * 商品名称 + * @mock 阿莫西林 + */ private String name;
💡 建议修复 - 可提升接口文档质量
✅ 审查通过
14/20 项检查通过(命名规范、Import规范、Mapper层、异常处理、缓存规范...)
📌 后续操作
| 命令 | 说明 |
|---|---|
fix all | 自动修复 #1, #3, #4(3个问题) |
fix #1 | 仅修复 Controller 业务逻辑问题 |
fix #3,#4 | 批量修复日志和校验问题 |
详细 #2 | 查看多数据源事务的详细说明 |
--- ## 规范来源 本审查规范基于以下规范文档编写: - [命名规范](../java/references/naming.md) - [代码风格](../java/references/coding-style.md) - [日志规范](../java/references/logging.md) - [异常处理](../java/references/exception.md) - [Controller 层](../java/references/controller.md) - [Service 层](../java/references/service.md) - [Mapper 层](../java/references/mapper.md) - [事务管理](../java/references/transaction.md) - [性能优化](../java/references/performance.md) - [缓存规范](../java/references/cache.md) - [并发控制](../java/references/concurrency.md) - [安全规范](../java/references/security.md) - [异步与消息](../java/references/async-mq.md) - [数据库设计](../java/references/database.md) - [接口文档](../java/references/api-doc.md)