Java代码审查技能
基于阿里巴巴Java开发手册和当前技能的编码规范内容,对Java代码进行全面审查和优化建议。
核心原则
- 强制项:必须遵守,违反会导致严重问题(如安全漏洞、系统崩溃)
- 推荐项:强烈建议遵守,提升代码质量和可维护性
- 参考项:根据实际情况灵活应用的最佳实践
检查维度
1. 命名规范(阿里巴巴规范)
1.1 通用命名规则
- 【强制】 类名使用UpperCamelCase风格,但DO/BO/DTO/VO/AO/PO等例外
- 【强制】 方法名、参数名、成员变量、局部变量使用lowerCamelCase风格
- 【强制】 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚
- 【强制】 抽象类命名使用Abstract或Base开头;异常类命名使用Exception结尾
- 【强制】 测试类命名以它要测试的类的名称开始,以Test结尾
- 【推荐】 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词
- 【参考】 枚举类名建议带上Enum后缀,枚举成员名称需要全大写,单词间用下划线隔开
1.2 特殊类型命名
- Service接口:XxxService,实现类:XxxServiceImpl
- Repository接口:XxxRepository,继承JpaRepository
- Controller类:XxxController
- DTO类:XxxDTO / XxxRequest / XxxResponse
- Entity类:Xxx(与数据库表对应)
- VO类:XxxVO(视图对象)
2. 代码注释规范
2.1 Javadoc规范
- 【强制】 类、类属性、类方法的注释必须使用Javadoc规范,使用/*内容/格式
- 【强制】 所有的抽象方法(包括接口中的方法)必须用Javadoc注释
- 【强制】 方法内部单行注释,在被注释语句上方另起一行,使用//注释
- 【强制】 所有的枚举类型字段必须要有注释,说明每个数据项的用途
- 【推荐】 与其"半吊子"英文来注释,不如用中文注释把问题说清楚
- 【参考】 代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等
2.2 注释内容要求
- 类注释:包含类功能说明、@author、@since、@version
- 方法注释:包含方法功能、@param参数说明、@return返回值、@throws异常说明
- 重要代码块:添加总结性注释说明业务逻辑
- 【禁止】 行尾注释(除非是简单的变量声明)
- 【禁止】 注释掉的代码应及时删除,通过版本控制管理历史代码
2.3 特殊注释标记
- 【推荐】 特殊注释标记,请注明标记人与标记时间
- TODO:表示待办事项
- FIXME:表示已知有问题,需要修复
3. 分层架构规范
3.1 分层职责
- 【强制】 Controller层:仅处理HTTP请求与响应,参数校验,禁止直接操作数据库,必须通过Service层调用
- 【强制】 Service层:业务逻辑实现,事务控制,必须通过Repository访问数据库,返回DTO而非实体类
- 【强制】 Repository层:数据访问接口,继承JpaRepository,使用@EntityGraph避免N+1查询问题
- 【强制】 Entity层:仅用于数据库交互,禁止直接返回给前端,需转换为DTO/VO
3.2 依赖方向
- 【强制】 上层可以调用下层,下层不能调用上层
- 【强制】 禁止跨层调用(如Controller直接调用Repository)
- 【推荐】 同层之间谨慎调用,避免循环依赖
3.3 依赖注入
- 【推荐】 使用@RequiredArgsConstructor + final字段实现构造器注入
- 【禁止】 避免在循环中创建对象或执行数据库查询
4. OOP规约(阿里巴巴规范)
4.1 访问控制
- 【强制】 避免通过一个类的对象引用访问此类的静态变量或静态方法,应直接用类名来访问
- 【强制】 所有的覆写方法,必须加@Override注解
- 【强制】 相同参数类型,相同业务含义,才可以使用Java的可变参数,避免使用Object
- 【强制】 外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响
4.2 equals与hashCode
- 【强制】 Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals
- 【强制】 所有整型包装类对象之间值的比较,全部使用equals方法比较
- 【推荐】 使用Objects.equals()进行安全的null值比较
4.3 集合处理
- 【强制】 关于hashCode和equals的处理,遵循如下规则:
- 只要重写equals,就必须重写hashCode
- Set存储的对象、Map的键对象,必须重写这两个方法
- 【强制】 ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常
- 【强制】 在subList场景中,高度注意对原集合元素的增加或删除,均会导致子列表的遍历、增加、删除产生ConcurrentModificationException异常
- 【推荐】 使用entrySet遍历Map类集合KV,而不是keySet方式进行遍历
- 【推荐】 高度注意Map类集合K/V能不能存储null值的情况
5. 并发处理(阿里巴巴规范)
5.1 线程安全
- 【强制】 获取单例对象需要保证线程安全,其中的方法也要保证线程安全
- 【强制】 创建线程或线程池时请指定有意义的线程名称,方便出错时回溯
- 【强制】 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程
- 【强制】 线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式
5.2 锁机制
- 【强制】 高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁
- 【推荐】 对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁
- 【参考】 在使用阻塞等待获取锁的方式中,必须在try代码块之外,并且在加锁方法与try代码块之间没有任何可能抛出异常的方法调用
5.3 线程池配置示例
// 推荐:手动创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, // 核心线程数
maximumPoolSize, // 最大线程数
keepAliveTime, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
workQueue, // 工作队列
threadFactory, // 线程工厂
handler // 拒绝策略
);
6. 控制语句(阿里巴巴规范)
6.1 if-else规范
- 【强制】 在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止
- 【强制】 在if/else/for/while/do语句中必须使用大括号,即使只有一行代码
- 【推荐】 表达异常的分支时,少用if-else方式,这种方式可以改写成:
if (condition) { ... return obj; } // 接着写else的业务逻辑代码; - 【推荐】 除常用方法(如getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句
6.2 循环规范
- 【强制】 循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展
- 【强制】 不要使用过时的Vector、Hashtable、Stack等集合类
- 【推荐】 不要在foreach循环里进行元素的remove/add操作,remove元素请使用Iterator方式
7. 前后端规约(阿里巴巴规范)
- 【强制】 前后端交互的API,需要明确协议、域名、路径、请求方法、请求内容、状态码、响应体
- 【强制】 服务端发生错误时,返回给前端的响应信息必须包含HTTP状态码、errorCode、errorMessage、用户提示信息四个部分
- 【推荐】 对于需要使用超大整数的场景,服务端一律使用String字符串类型返回,禁止使用Long类型
8. 异常处理规范(整合阿里巴巴规范)
8.1 异常捕获
- 【强制】 Java类库中定义的可以通过预检查方式规避的RuntimeException异常不应该通过catch的方式来处理
- 【强制】 异常不要用来做流程控制,条件控制
- 【强制】 catch时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码
- 【强制】 不要在finally块中使用return
8.2 异常处理最佳实践
- 【强制】 有try块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回滚事务
- 【推荐】 方法的返回值可以为null,不强制返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回null值
- 【推荐】 防止NPE,是程序员的基本修养,注意NPE产生的场景:
- 返回类型为基本数据类型,return包装数据类型的对象时,自动拆箱有可能产生NPE
- 数据库的查询结果可能为null
- 集合里的元素即使isNotEmpty,取出的数据元素也可能为null
- 远程调用返回对象时,一律要求进行空指针判断,防止NPE
- 对于Session中获取的数据,建议进行NPE检查,避免空指针
- 级联调用obj.getA().getB().getC();一连串调用,易产生NPE
8.3 统一异常处理
- 使用统一的ApiResponse包装响应
- 全局异常处理器使用@RestControllerAdvice
- Service层方法使用@Transactional注解,注意事务边界
- 自定义业务异常继承RuntimeException
9. 日志规约(阿里巴巴规范)
9.1 日志框架
- 【强制】 应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架SLF4J中的API
- 【强制】 日志文件至少保存15天,因为有些异常具备以"周"为频次发生的特点
9.2 日志级别
- 【强制】 生产环境禁止使用System.out或System.err输出
- 【强制】 异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字throws往上抛出
- 【推荐】 谨慎地记录日志。生产环境禁止输出debug日志;有选择地输出info日志
- 【推荐】 可以使用warn日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从
9.3 日志内容示例
// 推荐:使用占位符
log.info("用户{}登录成功,IP:{}", username, ipAddress);
// 禁止:字符串拼接
log.info("用户" + username + "登录成功,IP:" + ipAddress);
// 推荐:异常日志
log.error("处理订单失败,orderId:{}", orderId, exception);
10. MySQL数据库规约(阿里巴巴规范)
10.1 建表规约
- 【强制】 表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是unsigned tinyint(1表示是,0表示否)
- 【强制】 表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字
- 【强制】 表必备三字段:id, gmt_create, gmt_modified
- 【推荐】 表的命名最好是加上"业务名称_表的作用"
10.2 索引规约
- 【强制】 业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引
- 【强制】 超过三个表禁止join。需要join的字段,数据类型保持绝对一致;多表关联查询时,保证被关联的字段需要有索引
- 【推荐】 利用覆盖索引来进行查询操作,避免回表
- 【推荐】 建组合索引的时候,区分度最高的在最左边
10.3 SQL语句
- 【强制】 不要使用count(列名)或count(常量)来替代count(),count()是SQL92定义的标准统计行数的语法
- 【强制】 in操作能避免则避免,若实在避免不了,需要仔细评估in后边的集合元素数量,控制在1000个之内
- 【强制】 禁止使用存储过程,存储过程难以调试和扩展,更没有移植性
- 【推荐】 SQL性能优化的目标:至少要达到range级别,要求是ref级别,如果可以是consts最好
11. 工程结构(阿里巴巴规范)
11.1 应用分层
- 开放API层:可直接封装Service接口暴露成RPC接口;通过Web封装成http接口
- Controller层:处理HTTP请求,参数校验,返回统一格式
- Service层:业务逻辑处理,事务控制
- Manager层:通用业务处理层,对第三方平台封装,对Service层通用能力下沉
- Repository层:数据访问层,与底层MySQL进行数据交互
- Entity层:数据库实体对象
- DTO层:数据传输对象,用于服务间传输
- VO层:视图对象,用于前端展示
11.2 二方库依赖
- 【强制】 定义GAV遵从以下规则:
- GroupId格式:com.{公司/BU}.业务线.[子业务线],最多4级
- ArtifactId格式:产品线名-模块名
- Version:详细规定参考下方
- 【强制】 二方库版本号命名方式:主版本号.次版本号.修订号
12. 安全规约(阿里巴巴规范)
12.1 数据安全
- 【强制】 隶属于用户个人的页面或者功能必须进行权限控制校验
- 【强制】 用户敏感数据禁止直接展示,必须对展示数据进行脱敏
- 【强制】 用户输入的SQL参数严格使用参数绑定或者METADATA字段值限定,防止SQL注入
- 【强制】 用户请求传入的任何参数必须做有效性验证
12.2 密码安全
- 【强制】 禁止明文存储任何密码,必须使用加密算法(如BCrypt)
- 【强制】 禁止在代码、配置文件中硬编码密码、密钥、Token等敏感信息
12.3 其他安全
- 【推荐】 防止重放攻击。对于一次性使用的参数,使用后应立即失效
- 【推荐】 统一错误码映射,避免将系统内部错误信息直接暴露给用户
13. Spring Boot最佳实践
13.1 配置管理
- 【推荐】 使用@ConfigurationProperties绑定配置,而非@Value逐个注入
- 【推荐】 不同环境使用不同的配置文件(application-dev.yml, application-prod.yml)
- 【强制】 敏感配置(数据库密码、API密钥等)必须加密或使用环境变量
13.2 事务管理
- 【强制】 @Transactional注解只能应用于public方法
- 【强制】 避免在循环中频繁开启事务,应该批量处理
- 【推荐】 明确指定事务传播行为和隔离级别
- 【推荐】 对于只读操作,设置@Transactional(readOnly = true)提升性能
// 推荐:明确指定事务属性
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
rollbackFor = Exception.class)
public void createUser(UserDTO userDTO) {
// 业务逻辑
}
13.3 参数校验
- 【强制】 Controller层接收的参数必须使用@Valid或@Validated进行校验
- 【推荐】 使用自定义校验注解处理复杂业务规则
- 【推荐】 DTO中使用JSR-303注解(@NotNull, @NotBlank, @Email等)
// DTO示例
@Data
public class CreateUserRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度必须在2-20之间")
private String username;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
@NotNull(message = "年龄不能为空")
@Min(value = 1, message = "年龄必须大于0")
@Max(value = 150, message = "年龄不能超过150")
private Integer age;
}
13.4 性能优化
- 【推荐】 使用缓存减少数据库查询(Redis、Caffeine等)
- 【推荐】 批量操作代替单条操作(batch insert/update)
- 【推荐】 使用分页查询,避免一次性加载大量数据
- 【推荐】 异步处理耗时操作(@Async、消息队列)
- 【禁止】 避免N+1查询问题,使用@EntityGraph或JOIN FETCH
14. 单元测试规范
- 【推荐】 单元测试代码必须写在如下工程目录:src/test/java,不允许写在业务代码目录下
- 【推荐】 测试类、测试方法都需要添加明确的注释说明测试意图
- 【推荐】 和数据库相关的单元测试,可以设定自动回滚机制,不影响数据库数据
- 【推荐】 单元测试是可以重复执行的,不能受到外界环境的影响
- 【推荐】 对于单元测试,要保证测试粒度足够小,有助于精确定位问题
审查流程
- 读取代码文件:获取待审查的Java文件内容
- 分析代码结构:识别类、方法、字段及其关系
- 规范检查:对照上述维度逐项检查
- 问题识别:标记不符合规范的地方,标注严重程度(强制/推荐/参考)
- 生成报告:输出详细的审查报告和改进建议
输出格式
审查报告必须包含以下部分:
# Java代码审查报告
## 文件信息
- 文件路径:[文件路径]
- 类名:[类名]
- 检查时间:[时间]
## 总体评分
[优秀/良好/一般/需改进] - [评分]/100
## 问题汇总
| 序号 | 问题类型 | 严重程度 | 位置 | 问题描述 | 改进建议 |
|------|----------|----------|------|----------|----------|
| 1 | 命名规范 | 强制/推荐/参考 | 行号 | 具体问题 | 具体建议 |
## 详细分析
### 1. 命名规范检查
- [ ] 类名符合UpperCamelCase
- [ ] 方法/变量符合lowerCamelCase
- [ ] 常量符合UPPER_SNAKE_CASE
- [ ] 包名符合规范
- [ ] 特殊类型命名正确
### 2. 注释规范检查
- [ ] 类注释完整(包含@author、@since)
- [ ] 方法注释完整(包含@param、@return、@throws)
- [ ] 无行尾注释
- [ ] 重要代码块有注释
- [ ] 无注释掉的代码
### 3. 架构规范检查
- [ ] 分层正确(Controller/Service/Repository/Entity)
- [ ] 无跨层调用
- [ ] 依赖注入规范(如支持lombok推荐优先构造器注入,否则推荐使用@Autowired注入)
- [ ] 事务管理正确(@Transactional使用合理)
### 4. OOP规约检查
- [ ] 覆写方法有@Override
- [ ] equals和hashCode正确处理
- [ ] 集合使用规范
- [ ] 访问控制合理
### 5. 并发处理检查
- [ ] 线程池创建规范
- [ ] 线程安全措施得当
- [ ] 锁使用合理
### 6. 控制语句检查
- [ ] if/else/for等使用大括号
- [ ] switch case有break或注释
- [ ] 循环中无字符串拼接
- [ ] 无过时集合类使用
### 7. 异常处理检查
- [ ] 统一响应格式
- [ ] 异常处理完善(不在finally中return)
- [ ] NPE防护到位
- [ ] 事务回滚处理正确
### 8. 日志规范检查
- [ ] 使用SLF4J而非System.out
- [ ] 日志级别使用合理
- [ ] 使用占位符而非字符串拼接
- [ ] 异常日志包含堆栈信息
### 9. 数据库规范检查
- [ ] 表命名规范
- [ ] 索引使用合理
- [ ] SQL语句优化
- [ ] 无N+1查询问题
### 10. 安全检查
- [ ] 输入校验完善
- [ ] SQL防注入
- [ ] 敏感数据脱敏
- [ ] 无硬编码密码/密钥
### 11. Spring Boot最佳实践
- [ ] 参数校验使用@Valid
- [ ] 事务属性明确
- [ ] 配置管理规范
- [ ] 性能优化措施
## 改进建议代码示例
### 建议1:[问题简述]
**问题类型**:[强制/推荐/参考]
**影响范围**:[说明影响]
```java
// 改进前代码
[原代码]
// 改进后代码
[改进后代码]
// 改进说明
[详细说明为什么这样改进]
建议2:[问题简述]
...
总结
优点
- [列出代码中的亮点]
主要问题
- [列出最严重的3-5个问题]
改进优先级
- 高优先级:[必须立即修复的问题]
- 中优先级:[建议近期优化的问题]
- 低优先级:[可以逐步改进的问题]
整体评价
[对代码质量的总体评价,以及后续改进建议]
## 严重程度定义
- **强制**:违反阿里巴巴Java开发手册强制项,可能导致系统错误、安全漏洞或严重性能问题,必须修复
- **推荐**:违反推荐项,影响代码可读性、可维护性,或存在潜在风险,强烈建议修复
- **参考**:违反参考项或最佳实践,轻微的风格问题,建议改进但不强制
## 评分标准
- **优秀(90-100分)**:无强制项问题,推荐项问题≤2个,代码质量高
- **良好(75-89分)**:无强制项问题,推荐项问题≤5个,代码质量较好
- **一般(60-74分)**:强制项问题≤2个,或推荐项问题较多,需要改进
- **需改进(<60分)**:强制项问题>2个,或存在严重架构/安全问题,必须重构
## 使用示例
**示例1:审查Service层代码**
输入:请审查UserServiceImpl.java代码
输出:完整的审查报告,包含命名规范、注释缺失、架构规范、异常处理等问题的详细说明
**示例2:代码优化建议**
输入:帮我检查这段Java代码有什么问题
输出:问题列表(按强制/推荐/参考分类)+ 优化后的代码示例 + 改进说明
**示例3:规范合规性检查**
输入:检查这个Controller是否符合项目规范
输出:规范检查清单 + 不符合项(标注严重程度)+ 改进建议 + 代码示例
**示例4:安全审查**
输入:审查这段代码的安全性
输出:安全检查报告,重点关注SQL注入、XSS、敏感信息泄露、权限控制等问题
## 注意事项
1. **客观公正**:基于规范和事实进行审查,避免主观臆断
2. **建设性反馈**:不仅指出问题,更要提供可行的解决方案
3. **优先级排序**:按照强制>推荐>参考的优先级提出问题
4. **代码示例**:关键问题必须提供改进前后的代码对比
5. **持续改进**:鼓励渐进式改进,而非一次性完美主义
6. **结合实际**:考虑项目实际情况,灵活应用规范,避免教条主义
Scan to join WeChat group