前言
后端开发中,统一的编码规范能显著提升代码质量和团队协作效率。
本文按代码评审维度整理一套 Java 后端开发规范,便于 Review 时逐项对照检查。
适用于 Spring Boot + Java 17+ 技术栈,其他框架可按需调整。
审查维度概览
| 维度 |
分值 |
核心要求 |
| 命名规范 |
10 |
类/方法/变量命名风格统一,见名知意 |
| 项目结构与分层 |
15 |
目录清晰,Controller / Service / Repository 职责分明 |
| 代码风格 |
10 |
格式、注释、导入规范一致 |
| 业务逻辑与接口设计 |
25 |
职责边界清晰,参数校验完备,接口文档齐全 |
| 异常与错误处理 |
25 |
错误码统一,全局异常处理,信息对用户友好 |
| 工程实践 |
15 |
日志规范,无 N+1 / 魔法值等反模式 |
| 合计 |
100 |
|
命名规范
命名风格对照
| 命名方式 |
规则 |
示例 |
常见用途 |
| PascalCase(大驼峰) |
每个单词首字母大写 |
UserService、OrderController |
类名、接口名、枚举名 |
| camelCase(小驼峰) |
首单词小写,后续首字母大写 |
userName、getOrderList |
变量、方法名、参数 |
| kebab-case(短横线) |
全小写,单词间用 - 连接 |
user-service、order-detail |
配置 key、URL 路径 |
| snake_case(下划线) |
全小写,单词间用 _ 连接 |
user_name、create_time |
数据库字段 |
| UPPER_SNAKE_CASE |
全大写,单词间用 _ 连接 |
MAX_COUNT、API_BASE_URL |
常量 |
类、接口与文件
| 类型 |
规则 |
示例 |
| 类 / 接口 |
PascalCase |
UserService、UserRepository |
| 抽象类 |
以 Abstract 开头 |
AbstractBaseService |
| 实现类 |
以 Impl 结尾 |
UserServiceImpl |
| Controller |
类名 + Controller |
UserController.java |
| Service |
接口 + Service,实现 + ServiceImpl |
UserService、UserServiceImpl |
| 数据访问 |
以 Repository 或 Mapper 结尾 |
UserRepository |
| DTO / VO |
以 Dto / Vo 结尾 |
UserDto、UserVo |
1 2 3 4 5
| public class UserServiceImpl implements UserService { ... }
public class userService { ... }
|
方法与变量
方法命名(camelCase,动词开头):
| 场景 |
前缀 |
示例 |
| 查询 |
get、list、find |
getUserById、findUsersByStatus |
| 新增 |
add、create |
createUser |
| 修改 |
update |
updateUser |
| 删除 |
del、delete |
deleteUser |
| 判断 |
is、has、can |
isAdmin |
变量与常量:
- 变量使用 camelCase;常量使用 UPPER_SNAKE_CASE。
- 布尔变量以
is、has、can 开头,如 isDeleted、hasPermission。
- 集合变量使用复数形式,如
userList、orderIds。
1 2 3 4
| private static final int MAX_RETRY_COUNT = 3; private String userName; private boolean isDeleted; private List<Order> orderList;
|
项目结构与分层
目录约定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| src/main/java/com/example/project/ ├── controller/ # 控制器层 ├── service/ # 服务层 │ └── impl/ # 服务实现 ├── mapper/ # 数据访问层 ├── model/ # 数据模型 │ ├── entity/ # 数据库实体 │ ├── dto/ # 数据传输对象 │ └── vo/ # 视图对象 ├── config/ # 配置类 ├── constant/ # 常量 ├── enums/ # 枚举 ├── exception/ # 自定义异常 ├── util/ # 工具类 └── aspect/ # 切面
|
分层职责
| 层级 |
职责 |
| Controller |
接收请求、参数校验、调用 Service、返回响应 |
| Service |
业务逻辑处理、事务管理 |
| Repository / Mapper |
数据访问、SQL 编写 |
| Model |
Entity、DTO、VO 等数据模型定义 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @RestController @RequestMapping("/api/users") public class UserController {
private final UserService userService;
@GetMapping("/{id}") public Result<UserVO> getUser(@PathVariable Long id) { return Result.success(userService.getUserById(id)); } }
@Service public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
@Override public UserVO getUserById(Long id) { User user = userRepository.findById(id) .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); return convertToVO(user); } }
|
代码风格
格式规范
- 使用 4 空格缩进,不使用 Tab。
- 每行不超过 120 个字符,超长时换行对齐。
- 左大括号
{ 不换行,右大括号 } 独占一行。
- 方法之间、逻辑块之间空一行。
注释规范
- 类和公共方法必须添加 Javadoc,说明用途、参数、返回值、异常。
- 复杂逻辑添加行内注释,说明意图而非复述代码。
- TODO 格式:
// TODO(作者): 说明
导入规范
- 使用显式导入,禁止
import xxx.*。
- 导入顺序:
java.* → javax.* → 第三方库 → 项目内包。
- 静态导入仅用于常量和工具方法。
业务逻辑与接口设计
职责边界
- 单个接口不应把分支逻辑交给前端判断后分别处理,后端应封装完整业务语义。
- 接口中不写跨域逻辑,跨域统一在 Nginx 等网关层处理。
参数校验
使用 @Valid / @Validated 配合 JSR 303 注解,在 Controller 入口完成校验。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class UserDTO {
@NotBlank(message = "用户名不能为空") @Size(min = 2, max = 20, message = "用户名长度2-20个字符") private String userName;
@NotBlank(message = "邮箱不能为空") @Email(message = "邮箱格式不正确") private String email; }
@PostMapping public Result<Void> createUser(@Valid @RequestBody UserDTO dto) { userService.createUser(dto); return Result.success(); }
|
接口文档
- 所有对外接口必须有文档,不允许存在未文档化的接口。
- 接口按应用模块归类,而非按数据库表归类;多端差异明显时应拆分接口。
- 接口要有业务意义,避免机械式无意义的增删改查。
- 描述需说明业务场景与注意事项;参数标注必填与类型并提供示例值。
- 响应字段说明含义与类型,枚举值列出可选项。
- 接口变更时同步更新文档,返回值类型、必填项、字段完整性须与代码一致。
异常与错误处理
异常体系
- 业务异常继承
RuntimeException,信息通过枚举或常量管理,禁止硬编码。
- 参数校验异常使用
IllegalArgumentException。
- 不要捕获异常后静默吞掉,至少记录日志。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class BusinessException extends RuntimeException {
private final String code;
public BusinessException(ErrorCode errorCode) { super(errorCode.getMessage()); this.code = errorCode.getCode(); }
public BusinessException(ErrorCode errorCode, String message) { super(message); this.code = errorCode.getCode(); }
public String getCode() { return code; } }
|
错误码设计
格式:{模块}_{编号},如 USER_001、ORDER_002。
| 类型 |
前缀 |
范围 |
说明 |
| 成功 |
SUCCESS |
0 |
请求成功 |
| 系统错误 |
SYSTEM |
1xxx |
服务器内部错误、第三方异常 |
| 参数错误 |
PARAM |
2xxx |
参数校验失败、格式错误 |
| 认证授权 |
AUTH |
3xxx |
登录过期、权限不足 |
| 业务错误 |
按模块定义 |
各模块自定义 |
具体业务逻辑异常 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public enum ErrorCode {
SUCCESS("0", "成功"),
SYSTEM_ERROR("SYSTEM_1001", "系统繁忙,请稍后重试"), PARAM_INVALID("PARAM_2002", "参数无效"), AUTH_NO_PERMISSION("AUTH_3003", "没有操作权限"),
USER_NOT_FOUND("USER_001", "用户不存在"), ORDER_NOT_FOUND("ORDER_001", "订单不存在");
private final String code; private final String message;
ErrorCode(String code, String message) { this.code = code; this.message = message; }
public String getCode() { return code; } public String getMessage() { return message; } }
|
统一响应与全局处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| @Data public class Result<T> {
private String code; private String message; private T data;
public static <T> Result<T> success(T data) { Result<T> result = new Result<>(); result.setCode(ErrorCode.SUCCESS.getCode()); result.setMessage(ErrorCode.SUCCESS.getMessage()); result.setData(data); return result; }
public static <T> Result<T> fail(ErrorCode errorCode) { Result<T> result = new Result<>(); result.setCode(errorCode.getCode()); result.setMessage(errorCode.getMessage()); return result; } }
@RestControllerAdvice public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(BusinessException.class) public Result<Void> handleBusinessException(BusinessException e) { log.warn("业务异常: code={}, message={}", e.getCode(), e.getMessage()); Result<Void> result = new Result<>(); result.setCode(e.getCode()); result.setMessage(e.getMessage()); return result; }
@ExceptionHandler(IllegalArgumentException.class) public Result<Void> handleIllegalArgumentException(IllegalArgumentException e) { log.warn("参数异常: {}", e.getMessage()); return Result.fail(ErrorCode.PARAM_INVALID); }
@ExceptionHandler(Exception.class) public Result<Void> handleException(Exception e) { log.error("系统异常", e); return Result.fail(ErrorCode.SYSTEM_ERROR); } }
|
评审要点:错误码统一枚举管理;各模块前缀不同便于定位;错误信息对用户友好,不暴露技术细节;新增错误码同步更新枚举。
工程实践
日志规范
- 使用 SLF4J + Logback,禁止
System.out.println()。
- 生产环境默认
INFO,开发环境可调至 DEBUG。
- 日志输出至
logs/ 目录,格式包含时间、级别、类名、方法名、行号。
1 2 3 4 5 6 7 8 9 10 11 12
| private static final Logger log = LoggerFactory.getLogger(UserService.class);
public void createUser(UserDTO dto) { log.info("创建用户开始, userName={}", dto.getUserName()); try { User user = userRepository.save(convertToEntity(dto)); log.info("创建用户成功, userId={}", user.getId()); } catch (Exception e) { log.error("创建用户失败, userName={}", dto.getUserName(), e); throw e; } }
|
工具类设计
- 类使用
final 修饰,构造函数私有化,方法使用 static。
1 2 3 4 5 6 7 8 9 10
| public final class StringUtils {
private StringUtils() { throw new UnsupportedOperationException("工具类不允许实例化"); }
public static boolean isBlank(String str) { return str == null || str.trim().isEmpty(); } }
|
禁止项与反模式
| 禁止项 |
推荐做法 |
| 魔法值 |
提取为常量,如 MAX_PAGE_SIZE = 100 |
| 循环内单条查询(N+1) |
批量查询后 Map 关联 |
Service 层操作 HttpServletRequest |
在 Controller 层提取参数后传入 |
| 过深嵌套(> 3 层) |
拆分为独立方法 |
| 多表关联写法不当 |
评估 JOIN / 分步查询,避免性能隐患 |
1 2 3 4 5 6 7 8
| List<Long> userIds = orders.stream().map(Order::getUserId).collect(Collectors.toList()); Map<Long, User> userMap = userRepository.findByIds(userIds);
for (Order order : orders) { User user = userRepository.findById(order.getUserId()); }
|