deep-java-review

Polar Sponsor
爱发电 赞助
.NET 9.0

Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...

Java Code Review - Java项目代码评审工具

功能概述

Java Code Review - Java项目代码评审工具是一项面向实际任务的技能,主要用于智能化Java项目代码评审工具 ,完整流程(7步):;Git Diff 变更获取 - 拉取代码变更内容(支持 --cached / 分支对比);

核心要点

  • 完整上下文分析 - 分析调用链路,推断业务逻辑;
  • PRD文档生成 - 覆盖所有变更点(新增+修改);
  • 代码检测(P0/P1快速扫描) - 严重缺陷和代码缺陷快速定位;

使用与执行

细粒度代码审查清单。它将相关步骤、工具调用和结果整理方式集中到统一流程中,帮助使用者更快完成目标并减少重复操作。从功能定位来看,该技能强调把分散的操作要求整理成清晰、可复用的处理流程,使用户能够围绕既定目标快速准备输入、选择执行方式并获得结构化结果。

结果检查与注意事项

实际使用前应先确认任务范围、数据来源、运行环境、必要权限和关键参数,再依据技能说明逐步执行;若输入条件不完整,应先补齐信息或采用保守配置,避免因错误假设导致结果偏离需求。执行过程中需要关注工具调用是否成功、接口或依赖是否可用、输出格式是否符合预期,并对异常提示、缺失字段和边界情况进行处理;涉及批量任务时,还应保存进度,避免中断后重复操作。

Java Code Review - Java项目代码评审工具

概述

智能化Java项目代码评审工具,完整流程(7步):

  1. Git Diff 变更获取 - 拉取代码变更内容(支持 --cached / 分支对比)
  2. 完整上下文分析 - 分析调用链路,推断业务逻辑
  3. PRD文档生成 - 覆盖所有变更点(新增+修改)
  4. 代码检测(P0/P1快速扫描) - 严重缺陷和代码缺陷快速定位
  5. 细粒度代码审查清单 - 12类深度检查(A-L):Null安全、异常处理、Streams、并发、Java惯用法、资源管理、API设计、性能、测试、MyBatis/ORM、事务边界、SQL/DDL质量
  6. 多维度评分 - 代码质量评分
  7. Review报告生成 - 输出结构化报告

何时使用此技能

  • 用户需要进行代码评审
  • 用户需要理解变更的业务含义
  • 用户需要生成完整PRD文档
  • 用户需要评分和质量评估
  • 用户说 "review代码" / "检查PR" / "代码review" / "检查下代码"

第1步:Git Diff 变更获取

1.1 确定基准分支

优先询问用户,如用户未指定则使用以下策略:

  • 当前分支有上游跟踪 → 使用 @{u}(上游分支)
  • 存在 develop 分支 → 使用 develop
  • 存在 main/master 分支 → 使用 main/master
  • 否则 → 使用 HEAD~10(最近10次提交)

1.2 获取变更

# 场景1:工作区+暂存区 vs 基准分支(最常用)
git diff <基准分支>..HEAD --name-only
git diff <基准分支>..HEAD -U500
git diff <基准分支>..HEAD --stat

# 场景2:仅检查已暂存(staged)的变更
git diff --cached -U500
git diff --cached --stat

# 场景3:工作区未暂存变更
git diff -U500

1.3 收集信息

  • 新增的文件(新增功能)
  • 修改的文件(功能修改)
  • 删除的文件(功能删除)
  • 变更行数统计

第2步:完整上下文分析

2.1 确定入口点

对于每个变更的方法/类,向上追溯找到业务入口:

  • Controller层的HTTP入口(@PostMapping/@GetMapping)
  • Service层的业务入口方法
  • MQ消息入口(@RabbitListener/@KafkaListener)
  • 定时任务入口(@Scheduled)
  • 外部API入口(Feign Client)

2.2 追踪方法链

使用 lsp_find_references 分析每个关键方法:

入口点
  ↓ 调用
变更方法
  ↓ 调用
子方法1 → 子方法2 → ... → 数据库/外部服务

2.3 业务逻辑推断

基于完整链路推断业务逻辑:

变更方法:InventoryService.deduct()

完整链路:
Controller.createOrder() [下单入口]
  ↓
OrderService.create() [订单创建]
    ↓
InventoryService.deduct() [变更点] ← 库存扣减
    ↓
PaymentService.pay() [支付]
    ↓
NotificationService.notify() [通知]

推断业务需求:
用户下单 → 自动扣减库存 → 支付 → 通知
核心流程:订单+库存+支付+消息 四模块联动

第3步:PRD文档生成(核心能力)

重要:PRD必须覆盖所有变更点,包括新增功能和修改功能。

3.1 PRD文档结构

# 产品需求文档 PRD

## 1. 概述

### 1.1 产品名称
[模块名称-版本号]

### 1.2 需求背景
[为什么需要这个功能,解决什么问题]

### 1.3 需求范围
- 涉及模块:xxx
- 用户类型:xxx
- 使用场景:xxx

---

## 2. 功能需求

### 2.1 功能清单

| # | 功能点名称 | 功能类型 | 优先级 | 涉及文件 | 行号 |
|---|-----------|--------|--------|---------|-----|
| 1 | 功能点A | 新增 | P0 | XxxService.java | 1-100 |
| 2 | 功能点B | 新增 | P1 | XxxServiceImpl.java | 200-300 |
| 3 | 功能点C | 修改 | P2 | XxxConverter.java | 10-30 |
| 4 | 字段调整 | 修改 | P2 | DDL脚本 | - |

### 2.2 功能详情(每个变更点都需要详细描述)

#### 2.2.1 [功能点A-新增功能]

**功能类型**:新增

**功能描述**:
[详细描述这个功能做什么]

**业务规则**:
- 规则1
- 规则2

**核心链路**:

[入口] Controller.method() [POST /api/xxx] ↓ Service.method() [核心] 核心业务逻辑 ← 变更内容 ↓ DAO.method() [终点] 数据库/外部服务

**入参说明**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| param1 | String | 是 | xxx |
| param2 | Integer | 否 | xxx |

**出参说明**:
| 参数 | 类型 | 说明 |
|------|------|------|
| result1 | String | xxx |

**异常处理**:
| 异常场景 | 错误码 | 提示信息 |
|----------|--------|----------|
| 场景1 | E_xxx | xxx |

---

#### 2.2.2 [功能点B-修改功能]

**功能类型**:修改

**涉及文件**:`xxx.java`
**涉及行号**:xxx-xxx

**修改前**:
```java
// 原代码

修改后:

// 新代码

变更原因: [为什么要这样修改]

核心链路:

[入口] Controller.method() [POST /api/xxx]
  ↓ Service.method()
[核心] 修改的方法 ← 变更内容
  ↓ DAO.method()
[终点] 数据库/外部服务

影响分析:

  • 影响的上游:xxx
  • 影响的下游:xxx
  • 兼容性:是否向前兼容
  • 数据库:是否需要迁移

2.2.3 [功能点C-优化功能]

功能类型:优化

优化前: [优化前的描述]

优化后: [优化后的描述]

优化效果:

  • 性能提升:xxx%
  • 代码改进:xxx

3. 数据库变更

3.1 新增表

CREATE TABLE xxx (
  id VARCHAR(36) PRIMARY KEY,
  tenant_id VARCHAR(32) NOT NULL,
  org_id VARCHAR(32),
  doc_id VARCHAR(36) NOT NULL,
  retry_type VARCHAR(32),
  retry_count INT DEFAULT 0,
  last_retry_time DATETIME,
  create_time DATETIME NOT NULL,
  PRIMARY KEY (id),
  KEY idx_doc_id (doc_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='xxx表';

3.2 修改表

-- 修改字段长度
ALTER TABLE xxx MODIFY COLUMN goods_name VARCHAR(300) COMMENT '商品名称';

-- 新增字段
ALTER TABLE xxx ADD COLUMN operator VARCHAR(300) COMMENT '操作人';

3.3 数据迁移

[如有数据迁移需求,描述迁移脚本]

4. 接口设计

4.1 新增接口

接口名称 请求方式 路径 说明
接口A POST /api/xxx xxx

请求参数:

{
  "ids": ["xxx"],
  "operator": "xxx"
}

响应参数:

{
  "code": "0",
  "message": "成功",
  "data": null
}

4.2 修改接口

接口名称 请求方式 路径 说明
接口A POST /api/xxx 参数调整

变更说明: [描述接口参数变更]

5. 涉及的代码变更

5.1 新增文件

文件路径 说明
xxx.java 新增功能

5.2 修改文件

文件路径 修改内容
xxx.java 方法A新增、方法B修改

5.3 删除文件

文件路径 删除原因
xxx.java 功能废弃

6. 非功能需求

6.1 性能要求

  • 响应时间:< xxx ms
  • 并发能力:xxx TPS

6.2 安全要求

  • 权限校验:是/否
  • 数据加密:是/否

6.3 兼容性要求

  • 向前兼容:是/否
  • 多数据库支持:MySQL/Oracle/PostgreSQL

7. 测试要点

7.1 功能测试

  • 测试点1
  • 测试点2

7.2 异常测试

  • 测试点1
  • 测试点2

7.3 边界测试

  • 测试点1
  • 测试点2

7.4 回归测试

  • 测试点1(确认原有功能不受影响)
  • 测试点2

8. 上线注意点

  • 注意点1
  • 注意点2

9. 变更记录

版本 日期 变更内容 作者
v2.4.0.0 2026-04-30 初始版本 xxx
---

## 第4步:代码检测(P0/P1快速扫描)

在进入细粒度审查前,先快速扫描严重缺陷和代码缺陷。

### P0严重缺陷

| 检查项 | 检测模式 | 扣分 |
|--------|----------|------|
| 空指针 | `$OBJ.$METHOD()` obj未检查 | -10 |
| SQL注入 | `"SELECT " + $TABLE` 直接拼接 | -10 |
| 密钥泄露 | `password/token = "xxx"` | -10 |
| 事务缺失 | @Transactional 关键方法无事务 | -10 |

### P1代码缺陷

| 检查项 | 检测模式 | 扣分 |
|--------|----------|------|
| 资源泄漏 | new FileInputStream 未关闭 | -5 |
| 线程安全 | private static 共享变量 | -5 |
| 空catch | catch块为空或仅打印日志 | -5 |
| 循环查询 | for循环内数据库操作 | -5 |

### P2代码规范(快速扫描)

以下为不与第5步细粒度审查清单重叠的独立检查项:

| 检查项 | 检测模式 | 扣分 |
|--------|----------|------|
| 参数过多 | 方法参数超过3个 | -2 |
| 魔法数字 | 字面量未定义为常量 | -2 |
| 原始泛型 | List/Map 未指定类型参数 | -2 |

> **注意**:Streams滥用、Boolean参数、Optional.get()、equals缺hashCode、catch Exception 等 P2 检查项已整合到第5步对应分类中,此处不再重复。

---

## 第5步:细粒度代码审查清单 (Detailed Review Checklist)

### 审查策略

1. **Quick scan** - 理解变更意图,确定审查范围
2. **Checklist pass** - 按以下分类逐项检查
3. **Summary** - 按严重度汇总(Critical → Improvement → Minor)

### A. Null 安全

```java
// ❌ NPE 风险:链式调用不检查中间null
String name = user.getName().toUpperCase();

// ✅ 安全:Optional + map
String name = Optional.ofNullable(user.getName())
    .map(String::toUpperCase)
    .orElse("");

// ✅ 也可以:提前返回
if (user.getName() == null) {
    return "";
}
return user.getName().toUpperCase();

检查项:

  • 链式方法调用是否缺少null检查
  • 公共API缺少 @Nullable / @NonNull 注解
  • Optional.get() 没有 isPresent() 检查——改用 orElseThrow()
  • 方法返回 null 而非 Optional 或空集合
  • 对集合入参做null检查 → 使用 Objects.requireNonNull()

🔧 检测方法:

  • grep: Optional.get() → 检查是否有 isPresent() 先导
  • ast_grep_search: $OBJ.$METHOD() pattern 查找链式调用中缺少 null 守卫
  • lsp_find_references: 追踪方法返回 null 的调用方

建议:

  • 返回值可能为空时,使用 Optional 替代 null
  • 构造/方法入参使用 Objects.requireNonNull()
  • 返回空集合用 Collections.emptyList() 替代 null

B. 异常处理

// ❌ 吞掉异常(最坏实践)
try {
    process();
} catch (Exception e) {
    // 没有日志,没有处理
}

// ❌ 捕获范围过宽
catch (Exception e) { }
catch (Throwable t) { }

// ❌ 丢失原始异常栈(不传cause)
catch (IOException e) {
    throw new RuntimeException(e.getMessage());  // 丢失堆栈!
}

// ✅ 正确处理
catch (IOException e) {
    log.error("处理文件失败: {}", filename, e);
    throw new ProcessingException("文件处理失败", e);  // 传递cause
}

检查项:

  • 空 catch 块
  • 捕获范围过宽(Exception, Throwable)
  • 丢失原始异常(未链式传递 cause)
  • 用异常做流程控制
  • 受检异常泄露到API边界

🔧 检测方法:

  • ast_grep_search: catch (Exception 和 catch (Throwable → 宽泛捕获
  • grep: catchs*( 带上下文行查找空 catch 块
  • grep: throw new.*getMessage() → 丢失原始异常栈

建议:

  • 日志同时记录上下文 + 完整栈信息
  • 使用具体的异常子类
  • 领域错误使用自定义异常

C. Streams & 集合操作

// ❌ 遍历中修改集合 → ConcurrentModificationException
for (Item item : items) {
    if (item.isExpired()) {
        items.remove(item);
    }
}

// ✅ 使用 removeIf
items.removeIf(Item::isExpired);

// ❌ 简单逻辑滥用Stream
list.stream().forEach(System.out::println);

// ✅ 简单场景直接用for循环
for (Item item : list) {
    System.out.println(item);
}

// ❌ 假设 Collectors.toList() 返回可变List
List names = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());
names.add("extra");  // 可能不可变!

// ✅ 显式要求可变集合
List names = users.stream()
    .map(User::getName)
    .collect(Collectors.toCollection(ArrayList::new));

检查项:

  • 遍历时修改集合(用 removeIf() 替代)
  • 简单操作用Stream替代for循环(可读性反而差)
  • Collectors.toList() 假定返回可变列表
  • 未使用 List.of(), Set.of(), Map.of() 创建不可变集合
  • 不理解就乱用并行流(parallelStream)

🔧 检测方法:

  • ast_grep_search: .stream().forEach → 可能的滥用模式
  • grep: .remove( 在 fors*( 块内 → 遍历时修改
  • grep: .collect(Collectors.toList()) → 检查后续是否有 add/remove 操作

建议:

  • 防御性拷贝用 List.copyOf()
  • Stream 用于数据转换,循环用于副作用操作
  • 简单逻辑(1-2行映射)用Stream,复杂逻辑用for循环

D. 并发安全

// ❌ 非线程安全的集合
private Map cache = new HashMap<>();

// ✅ 线程安全集合
private Map cache = new ConcurrentHashMap<>();

// ❌ check-then-act 竞态条件
if (!map.containsKey(key)) {
    map.put(key, computeValue());  // 多线程下可能重复计算
}

// ✅ 原子操作
map.computeIfAbsent(key, k -> computeValue());

// ❌ 双重检查锁(没有volatile则broken)
if (instance == null) {
    synchronized(this) {
        if (instance == null) {
            instance = new Instance();  // 可能看到半初始化对象
        }
    }
}

// ✅ 使用静态内部类 / volatile
private volatile Instance instance;

检查项:

  • 共享可变状态缺少同步
  • check-then-act 操作缺少原子性
  • 共享变量缺少 volatile
  • 在非final对象上同步
  • 线程不安全的懒初始化

🔧 检测方法:

  • grep: private static.*Map|List|Set → 静态共享集合
  • ast_grep_search: synchronized( → 检查同步对象是否为 final
  • grep: if (.*== null) 在 synchronized 块内 → 双重检查锁

建议:

  • 优先使用不可变对象
  • 优先用 java.util.concurrent 并发集合类
  • 简单场景用 AtomicReference, AtomicInteger
  • 考虑添加 @ThreadSafe / @NotThreadSafe 注解

E. Java 惯用法 (equals/hashCode/toString/Builder)

equals & hashCode:

// ❌ 只重写equals不重写hashCode → 破坏HashMap契约!
@Override
public boolean equals(Object o) { ... }
// hashCode 缺失!

// ❌ hashCode用了可变字段 → 放入HashMap后字段变了就找不到了
@Override
public int hashCode() {
    return Objects.hash(id, mutableField);  // 危险!
}

// ✅ 同时重写,只用不可变字段
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof User user)) return false;
    return Objects.equals(id, user.id);
}

@Override
public int hashCode() {
    return Objects.hash(id);
}

toString:

// ❌ 包含敏感数据
return "User{password='" + password + "'}";

// ✅ 调试友好但安全
return "User{id=" + id + ", name='" + name + "'}";

Builder模式:

// ✅ 当构造参数 > 3 时推荐
User user = User.builder()
    .name("张三")
    .email("zhangsan@example.com")
    .build();

检查项:

  • equals 有但 hashCode 缺失 → 确定要破坏HashMap/HashSet吗?
  • hashCode 使用了可变字段
  • 领域对象缺少 toString(调试困难)
  • 构造参数 > 3-4 → 建议Builder
  • 未使用 Java 16+ instanceof pattern matching

🔧 检测方法:

  • ast_grep_search: public boolean equals → 检查同文件是否有 public int hashCode
  • grep: Objects.hash(.*this. → hashCode 使用了实例字段(可能可变)
  • grep: password|secret|token 在 toString() 方法内 → 敏感数据泄露

F. 资源管理 (Try-with-resources)

// ❌ 资源泄漏
FileInputStream fis = new FileInputStream(file);
// ... 抛异常前没close

// ✅ try-with-resources
try (FileInputStream fis = new FileInputStream(file)) {
    // 自动关闭
}

// ❌ 多个资源嵌套,可能内层未关闭
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
    // 如果 BufferedWriter 构造失败,FileWriter 可能泄漏
}

// ✅ 独立声明
try (FileWriter fw = new FileWriter(file);
     BufferedWriter writer = new BufferedWriter(fw)) {
    // 两者都被保证关闭
}

检查项:

  • Closeable/AutoCloseable 未使用 try-with-resources
  • 数据库连接/Statement/ResultSet 未正确关闭
  • 嵌套资源声明顺序错误

🔧 检测方法:

  • grep: new (File|Input|Output|Reader|Writer|Connection|Statement) → 检查是否在 try-with-resources 内
  • ast_grep_search: new $CLOSEABLE($$$) 不在 try ( 块内 → 资源泄漏风险

强制规则:任何实现了 Closeable/AutoCloseable 的资源,必须使用 try-with-resources。

G. API 设计

// ❌ Boolean参数 → 调用端看不懂语义
process(data, true, false);

// ✅ 使用枚举
process(data, ProcessMode.ASYNC, ErrorHandling.STRICT);

// ❌ 找不到返回null (调用方不知道)
public User findById(Long id) {
    return users.get(id);  // 可能null
}

// ✅ 语义明确的 Optional
public Optional findById(Long id) {
    return Optional.ofNullable(users.get(id));
}

// ❌ 入参允许null → 调用方不知道能不能传null
public void process(List items) {
    if (items == null) items = Collections.emptyList();
}

// ✅ 明确要求非null
public void process(List items) {
    Objects.requireNonNull(items, "items must not be null");
}

检查项:

  • Boolean参数 → 建议用枚举替代
  • 方法参数 > 3 → 考虑参数对象
  • 同一模块内null处理不一致
  • 公共API入参缺少校验(Objects.requireNonNull)

🔧 检测方法:

  • ast_grep_search: $VISIBILITY $RET $NAME(boolean $PARAM → Boolean 参数
  • grep: public.*(.*,.*,.*,.*, → 参数超过3个的方法签名
  • grep: Objects.requireNonNull → 检查公共方法是否都有入参校验

H. 性能注意事项

// ❌ 循环内String拼接 → 每次创建新String对象
String result = "";
for (String s : strings) {
    result += s;
}

// ✅ StringBuilder
StringBuilder sb = new StringBuilder();
for (String s : strings) {
    sb.append(s);
}

// ❌ 循环内编译正则 → 每次都重新编译
for (String line : lines) {
    if (line.matches("pattern.*")) { }
}

// ✅ 预编译为静态常量
private static final Pattern PATTERN = Pattern.compile("pattern.*");
for (String line : lines) {
    if (PATTERN.matcher(line).matches()) { }
}

// ❌ N+1 查询
for (User user : users) {
    List orders = orderRepo.findByUserId(user.getId());
}

// ✅ 批量查询
Map> ordersByUser = orderRepo.findByUserIds(userIds);

检查项:

  • 循环内 String 拼接 → 用 StringBuilder
  • 循环内正则编译 → 预编译为 Pattern 常量
  • N+1 查询模式
  • 循环内创建可复用对象
  • 未使用原始类型Stream(IntStream, LongStream)

🔧 检测方法:

  • grep: += 在 fors*( 块内 → 循环内 String 拼接
  • grep: .matches( 在 fors*( 块内 → 循环内正则编译
  • ast_grep_search: for.*{.*.(select|find|query) → N+1 查询模式

I. 测试提示

建议为以下场景补充测试:

  • Null入参
  • 空集合入参
  • 边界值
  • 异常路径
  • 并发访问(如适用)

🔧 检测方法:

  • glob: **/src/test/**/*Test*.java → 检查是否有对应测试文件
  • grep: @Test → 统计测试方法数量 vs 变更方法数量

J. MyBatis/ORM 检查

// ❌ N+1 查询:循环内逐条查询
for (Order order : orders) {
    List items = orderItemMapper.selectByOrderId(order.getId());
    order.setItems(items);
}

// ✅ 批量查询 + 内存组装
List orderIds = orders.stream().map(Order::getId).collect(Collectors.toList());
List allItems = orderItemMapper.selectByOrderIds(orderIds);
Map> itemsMap = allItems.stream()
    .collect(Collectors.groupingBy(OrderItem::getOrderId));
orders.forEach(o -> o.setItems(itemsMap.getOrDefault(o.getId(), Collections.emptyList())));

// ❌ 分页插件滥用:先查全部再内存分页
List allUsers = userMapper.selectAll();
List page = allUsers.subList(offset, offset + limit);

// ✅ 使用分页插件
PageHelper.startPage(pageNum, pageSize);
List users = userMapper.selectByCondition(condition);

检查项:

  • XML映射ID与方法名不一致
  • N+1 查询模式(循环内调用 Mapper)
  • 分页插件滥用(全量查询后内存分页)
  • 一级缓存/二级缓存不当使用(脏读风险)
  • 条件缺少对空字符串的判断
  • #{} 与 ${} 混用(${} 有 SQL 注入风险)

🔧 检测方法:

  • grep: 在 *.xml → 检查 id 命名是否与方法名一致
  • ast_grep_search: for.*{.*Mapper.|for.*{.*mapper. → N+1 查询模式
  • grep: PageHelper.startPage → 检查是否紧跟 Mapper 调用(分页生效)
  • grep: ${ 在 *.xml → SQL 注入风险(应使用 #{})

K. 事务边界检查

// ❌ 事务传播级别错误:REQUIRES_NEW 导致外层事务回滚失效
@Transactional(propagation = Propagation.REQUIRED)
public void processOrder(Order order) {
    orderMapper.update(order);
    paymentService.pay(order);  // 内部 REQUIRES_NEW,pay 成功但外层回滚
}

// ❌ 同类方法调用事务失效(Spring AOP 代理限制)
public void batchProcess(List orders) {
    for (Order order : orders) {
        this.processSingle(order);  // @Transactional 不生效!
    }
}

// ✅ 注入自身代理或拆分到不同类
@Transactional
public void processSingle(Order order) { ... }

// ❌ 长事务导致锁竞争
@Transactional
public void exportReport() {
    // 大量查询 + 文件生成 + 邮件发送 都在一个事务内
    List data = dataMapper.selectAll();  // 持有数据库连接
    File file = generateExcel(data);           // 耗时操作
    emailService.send(file);                   // 外部IO
}

检查项:

  • 事务传播级别错误(REQUIRES_NEW 嵌套回滚边界)
  • 同类方法调用导致事务失效
  • 长事务(事务内包含外部IO、大量计算)
  • @Transactional 仅用于 public 方法(非 public 不生效)
  • 事务内捕获异常未重新抛出(吞掉回滚)
  • readOnly=true 未设置(查询方法应标记只读)

🔧 检测方法:

  • grep: @Transactional → 检查 propagation 属性
  • grep: REQUIRES_NEW → 嵌套事务回滚边界
  • grep: @Transactional 在非 public 方法上 → 事务不生效
  • ast_grep_search: @Transactional.*{.*(?:send|http|file|sleep) → 长事务嫌疑

L. SQL/DDL 质量检查

-- ❌ 缺索引:大表无索引导致全表扫描
CREATE TABLE t_order (
  id BIGINT PRIMARY KEY,
  user_id BIGINT NOT NULL,
  order_no VARCHAR(64) NOT NULL,
  status TINYINT,
  create_time DATETIME
) ENGINE=InnoDB;  -- 缺少 user_id 和 order_no 索引!

-- ✅ 合理索引
CREATE TABLE t_order (
  id BIGINT PRIMARY KEY,
  user_id BIGINT NOT NULL,
  order_no VARCHAR(64) NOT NULL,
  status TINYINT DEFAULT 0,
  create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  KEY idx_user_id (user_id),
  UNIQUE KEY uk_order_no (order_no),
  KEY idx_status_create (status, create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

-- ❌ 字段类型不当
ALTER TABLE t_order ADD COLUMN amount VARCHAR(32);  -- 金额应用 DECIMAL

-- ✅ 正确类型
ALTER TABLE t_order ADD COLUMN amount DECIMAL(18,2) NOT NULL DEFAULT 0.00 COMMENT '订单金额';

检查项:

  • 缺索引(WHERE/JOIN/ORDER BY 字段无索引)
  • 字段类型不当(金额用 VARCHAR、状态用 VARCHAR 而非 TINYINT)
  • 缺 COMMENT(表、字段无注释)
  • 字符集不一致(表间 JOIN 字段字符集不同)
  • DDL 缺少回滚脚本
  • VARCHAR 长度不合理(过长或过短)
  • 缺默认值(NOT NULL 字段无 DEFAULT)

🔧 检测方法:

  • grep: CREATE TABLE 无 COMMENT → 缺表注释
  • grep: ALTER TABLE → 检查是否有对应回滚脚本
  • grep: VARCHAR( → 检查长度是否合理(如 VARCHAR(1) 或 VARCHAR(5000))
  • grep: amount|price|money.*VARCHAR → 金额字段类型不当

严重度分级参考

严重度 标准 对应评分
Critical 安全漏洞、可能丢失数据、生产环境必崩 P0 (-10)
High 大概率Bug、严重性能问题、破坏API契约 P1 (-5)
Medium 代码坏味、可维护性问题、缺少最佳实践 P2 (-2)
Low 风格问题、微优化、建议性 P2 (-2)

第6步:多维度评分系统

评分维度

维度 权重 计算方式
代码质量 20% (100 - 扣分) * 权重
安全性 25% (100 - P0问题*10) * 权重
性能 20% (100 - 性能问题*8) * 权重
可维护性 15% (100 - 规范问题*5) * 权重
业务风险 20% (100 - 风险问题*10) * 权重

扣分规则

问题类型 扣分
P0问题 -10分/个
P1问题 -5分/个
P2建议 -2分/个

最终评级

等级 分数 颜色 行动
S 90-100 🟢 绿 可合并
A 80-89 🟢 绿 可合并
B 70-79 🟡 黄 需要确认
C 60-69 🟠 橙 需要修改
D <60 🔴 红 拒绝合并

第7步:Review报告生成

# 代码评审报告

## 基本信息
- 项目名称:xxx
- 评审范围:xxx → xxx
- 变更文件数:N
- 变更行数:+X / -Y

## 📊 质量评分

| 维度 | 得分 | 评级 |
|------|------|------|
| 代码质量 | 85 | A |
| 安全性 | 90 | S |
| 性能 | 80 | A |
| 可维护性 | 75 | B |
| 业务风险 | 70 | B |
| **总分** | **82** | **A** |

## 📋 需求汇总

### 需求1:[功能点A]
- 涉及文件:XxxService.java (新增100行)
- 变更类型:新增功能
- 入口:XxxController.create() (POST /api/xxx)
- 核心链路:

[入口] XxxController.create() ↓ XxxService.process() [核心] XxxHandler.handle() ← 变更点 ↓ XxxRepository.save() [终点] 数据库持久化

- 风险等级:🟡 中

### 需求2:[功能点B]
- 涉及文件:XxxServiceImpl.java, XxxController.java
- 变更类型:新增功能
- 入口:POST /api/xxx/action
- 核心链路:

[入口] XxxController.action() [核心] XxxServiceImpl.action() ← 变更点 ↓ 逻辑处理 [终点] 数据库更新

- 风险等级:🟡 中

## 🔴 严重问题 (P0)
- [ ] 问题1
- 文件:xxx.java
- 行号:xxx
- 修复方案

## 🟡 中等问题 (P1)
- [ ] 问题1
- 文件:xxx.java
- 优化方案

## 🟢 建议优化 (P2)
- [ ] 优化1

## 评审结论
- 总体评级:S/A/B/C/D
- 是否可以合并:是/需修改/拒绝

输出

  1. Review报告 → code-review-report.md(含评分 + 业务分析 + 细粒度代码检查)
  2. PRD文档 → prd-{版本号}.md
  3. 控制台摘要 → 评分+问题统计

Review 执行流程总结

Trigger: "review代码" / "检查PR" / "代码评审"

Step 1: git diff 获取变更
  ├─ 确定基准分支(询问用户 / 自动检测)
  ├─ 支持 --cached(仅暂存区)/ 分支对比
  ├─ 统计变更文件和行数
  └─ 识别变更类型(新增/修改/删除)

Step 2: 上下文分析
  ├─ lsp_find_references 追踪调用链
  ├─ 确定业务入口(Controller/MQ/定时任务/Feign)
  └─ 推断业务逻辑

Step 3: PRD文档生成(覆盖所有变更点)
  ├─ 功能清单(含文件+行号)
  ├─ 核心链路图
  ├─ 数据库变更
  └─ 接口设计

Step 4: 代码检测(P0/P1快速扫描)
  ├─ P0严重缺陷(NPE/SQL注入/密钥/事务)
  ├─ P1代码缺陷(资源泄漏/线程安全/空catch/循环查询)
  └─ P2代码规范(参数过多/魔法数字/原始泛型)

Step 5: 细粒度审查清单(12类深度检查)
  ├─ A. Null安全
  ├─ B. 异常处理
  ├─ C. Streams & 集合
  ├─ D. 并发安全
  ├─ E. Java惯用法 (equals/hashCode)
  ├─ F. 资源管理 (try-with-resources)
  ├─ G. API设计
  ├─ H. 性能
  ├─ I. 测试提示
  ├─ J. MyBatis/ORM 检查
  ├─ K. 事务边界检查
  └─ L. SQL/DDL 质量检查

Step 6: 多维度评分
  ├─ 代码质量 20%
  ├─ 安全性 25%
  ├─ 性能 20%
  ├─ 可维护性 15%
  └─ 业务风险 20%

Step 7: 生成Review报告
  ├─ 基本信息
  ├─ 质量评分(含维度拆分)
  ├─ 需求汇总(含调用链路)
  ├─ 问题清单(Critical/High/Medium/Low)
  └─ 评审结论

PRD生成 Checklist

生成PRD时,每个变更点都必须包含:

  • 功能名称
  • 功能类型(新增/修改/删除/优化)
  • 涉及文件和行号
  • 核心业务链路
  • 入参/出参说明
  • 异常处理
  • 数据库变更(如有)
  • 测试要点

相关专题

更多
IntelliJ IDEA调试技巧汇总
IntelliJ IDEA调试技巧汇总

本合集讲解 IDEA 断点调试、条件断点、远程调试、微服务链路调试、内存快照分析、异常定位高级用法,解决 Java 项目排查难题。

2026.08.11

51

15

Java Spring Boot多租户架构设计与数据隔离实战
Java Spring Boot多租户架构设计与数据隔离实战

本专题聚焦 Java 企业级系统中的多租户架构设计,讲解数据隔离策略、租户路由机制、数据库分库分表方案以及权限控制模型。通过真实 SaaS 系统案例,帮助开发者构建可扩展的多租户应用架构。

2026.06.29

216

13

Java Spring Boot3响应式编程与WebFlux实战
Java Spring Boot3响应式编程与WebFlux实战

本专题围绕Spring Boot 3中的响应式编程体系展开,系统讲解 WebFlux 的非阻塞模型、Reactive Streams、Mono 与 Flux 使用方式,以及在高并发场景下的性能优势。结合真实后端案例,帮助开发者构建低延迟、高吞吐的响应式服务架构。

2026.06.22

180

17

Java分布式事务与Seata实战架构设计
Java分布式事务与Seata实战架构设计

本专题围绕 Java 分布式系统中的事务一致性问题展开,系统讲解分布式事务的核心理论与解决方案。内容包括 Seata AT/TCC 模式原理、事务协调机制、服务调用链一致性保障以及高并发场景下的数据一致性设计策略。通过真实微服务案例,帮助开发者构建可靠的分布式事务处理体系。

2026.06.16

161

12

Java消息队列与异步解耦实战
Java消息队列与异步解耦实战

系统讲解 Java 中消息队列的集成与应用实践,涵盖消息队列的核心概念(生产者/消费者/Topic/Queue/Exchange)、RabbitMQ 的交换机类型与 Spring AMQP 集成、Kafka 分区/副本/消费者组机制与 Spring Kafka 配置、RocketMQ 事务消息与顺序消息、消息可靠投递(确认机制/持久化/重试)、消费端幂等性保障(唯一ID/去重表)、死信队列与延迟消息处理、消息积压监控与扩容策略,帮助开发

2026.05.18

235

29

Java缓存技术与Redis集成教程合集
Java缓存技术与Redis集成教程合集

系统讲解 Java 项目中缓存技术的选型与实践,涵盖本地缓存方案(HashMap 手动缓存/Guava Cache/Caffeine 高性能缓存)的使用与淘汰策略配置、Redis 数据结构(String/Hash/List/Set/ZSet)在缓存场景的应用、Jedis / Lettuce / Redisson 客户端选型、Spring Cache 注解(@Cacheable/@CachePut/@CacheEvict)的声明式缓存、缓

2026.05.15

402

34

Java容器化部署与Docker实践教程合集
Java容器化部署与Docker实践教程合集

聚焦 Java 应用的容器化与云原生部署,讲解 Dockerfile 编写规范与 Java 应用镜像构建、多阶段构建(Multi-stage Build)减小镜像体积、Jib / Buildpacks 免 Dockerfile 镜像构建方案、JVM 容器感知参数(-XX:MaxRAMPercentage)配置、Docker Compose 编排多服务(应用 + MySQL + Redis)、容器健康检查与资源限制、Kubernetes

2026.05.11

280

26

Java Maven/Gradle 构建与依赖管理合集
Java Maven/Gradle 构建与依赖管理合集

系统讲解 Java 项目构建工具的使用与进阶配置,涵盖 Maven 的 POM 文件结构、生命周期(clean/compile/package/install/deploy)与插件机制、依赖范围(compile/provided/test/runtime)与传递依赖管理、多模块聚合与继承、私有 Nexus 仓库发布,以及 Gradle 的 Groovy / Kotlin DSL 语法、Task 自定义与增量构建、依赖版本目录(Versi

2026.05.09

229

25

Java 函数式接口与 Lambda 进阶技巧
Java 函数式接口与 Lambda 进阶技巧

在 Java Stream 基础之上进一步深入函数式编程,涵盖 @FunctionalInterface 注解与自定义函数式接口设计、四种方法引用(静态/实例/对象/构造器)的使用场景、Lambda 中的 effectively final 变量捕获规则、函数组合(andThen/compose)与柯里化技巧、Comparator 链式比较器构建、CompletableFuture 异步函数式编排、Reactor / RxJava 响应

2026.05.08

248

28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习