【Spring Boot实战系列】代码review太痛苦?这套开发规范让团队效率翻倍

前言
本系列文章基于 youlai-boot 开源项目实践,一个开箱即用的 Spring Boot 后台管理系统。
之前接手过一个项目,代码风格五花八门:有的用 getUserById,有的用 selectUser;有的返回 Result,有的直接返回对象;错误码有的用数字,有的用字符串…改个功能先读半天代码,效率极低。
后来项目组强制推行统一规范,写了份开发手册,新人入职先看手册。效果立竿见影——代码风格一致了,review 效率上去了,维护成本下来了。
youlai-boot 这套规范参考了阿里巴巴 Java 开发手册,也融入了项目组的实战经验。
命名规范:统一风格减少认知负担
包命名
全部小写,不用分隔符:
com.youlai.boot.system ✅
com.youlai.boot.systemService ❌
类命名
| 类型 | 规则 | 示例 |
|---|---|---|
| 控制器 | XxxController | UserController |
| 业务接口 | XxxService | UserService |
| 业务实现 | XxxServiceImpl | UserServiceImpl |
| Mapper | XxxMapper | UserMapper |
| 实体类 | Xxx | User |
| 表单对象 | XxxForm | UserForm |
| 查询对象 | XxxQuery | UserQuery |
| 视图对象 | XxxVO | UserPageVO |
| 枚举 | XxxEnum | DataScopeEnum |
| 常量 | XxxConstants | SystemConstants |
这套命名 Spring 项目基本都这么用,保持一致就好。
方法命名:一眼看出用途
查询方法:
| 前缀 | 用途 | 示例 |
|---|---|---|
| get | 单个查询 | getUserById() |
| list | 列表查询 | listUsers() |
| page/getPage | 分页查询 | getUserPage() |
| exists | 存在判断 | existsByUsername() |
| count | 统计数量 | countUsers() |
写操作:
| 前缀 | 用途 | 示例 |
|---|---|---|
| save | 新增 | saveUser() |
| update | 修改 | updateUser() |
| delete | 删除 | deleteUser() |
别用 add、modify、remove,保持一致性。看到 get 就知道是查,看到 save 就知道是新增。
变量命名
private UserService userService; // 成员变量,驼峰
User user = userService.getById(id); // 局部变量
public static final String DEFAULT_PASSWORD = "123456"; // 常量,全大写
private boolean isRoot; // 布尔值,is/has/can 前缀
分层架构:职责清晰避免混乱
各层职责
| 层 | 职责 | 禁止 |
|---|---|---|
| Controller | 参数校验、调用 Service、组装返回 | 直接调 Mapper |
| Service | 业务逻辑、事务控制 | 跨业务调 Mapper |
| Mapper | 数据库访问 | 写业务逻辑 |
Service 调用原则
核心规则:Service 只调自己业务域的 Mapper,跨业务通过 Service 交互。
// ✅ 正确
@Service
public class UserServiceImpl {
private final UserMapper userMapper;
private final RoleService roleService; // 通过 Service 拿角色数据
}
// ❌ 错误:UserService 直接注入 RoleMapper
@Service
public class UserServiceImpl {
private final RoleMapper roleMapper; // 禁止跨业务注入
}
这条规则看着简单,但能避免 Service 里各种 Mapper 乱注入的混乱局面。
RESTful API:约定优于配置
URL 设计
- 名词复数:
/api/v1/users✅ - 避免动词:
/api/v1/getUsers❌ - 层级不超过 3 层
HTTP 方法映射
| 方法 | 操作 | 示例 |
|---|---|---|
| GET | 查询 | GET /api/v1/users |
| POST | 新增 | POST /api/v1/users |
| PUT | 全量修改 | PUT /api/v1/users/{id} |
| PATCH | 部分修改 | PATCH /api/v1/users/{id}/status |
| DELETE | 删除 | DELETE /api/v1/users/{id} |
Controller 示例
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
@GetMapping
public PageResult<UserPageVO> list(UserQuery query) { }
@GetMapping("/{id}")
public Result<User> get(@PathVariable Long id) { }
@PostMapping
@PreAuthorize("@ss.hasPerm('sys:user:create')")
public Result<Void> save(@RequestBody @Valid UserForm form) { }
@PutMapping("/{id}")
@PreAuthorize("@ss.hasPerm('sys:user:update')")
public Result<Void> update(@PathVariable Long id, @RequestBody @Valid UserForm form) { }
@DeleteMapping("/{id}")
@PreAuthorize("@ss.hasPerm('sys:user:delete')")
public Result<Void> delete(@PathVariable Long id) { }
}
特殊操作转名词:POST /api/v1/users/{id}/password/reset(重置密码)
权限标识:一目了然
命名格式
模块:资源:操作
示例
sys:user:list # 用户列表
sys:user:create # 新增用户
sys:user:update # 修改用户
sys:user:delete # 删除用户
sys:user:export # 导出用户
通配符:sys:user:* 表示用户模块所有权限。
这套标识在代码 review 和权限配置时都方便,沟通时直接说"用户删除权限"就行。
错误码:快速定位问题
参考阿里巴巴错误码规范,5 位字符串:
| 前缀 | 说明 |
|---|---|
| A | 用户端错误(参数错误、认证失败等) |
| B | 系统错误 |
| C | 第三方服务错误 |
常用错误码:
| 错误码 | 说明 |
|---|---|
| 00000 | 成功 |
| A0200 | 登录异常 |
| A0230 | Token 无效 |
| A0301 | 权限不足 |
| A0400 | 参数错误 |
| A0506 | 重复提交 |
| B0001 | 系统异常 |
好处是沟通时直接说错误码,不用描述半天。“用户遇到 A0301 了”——马上知道是权限问题。
参数校验:Controller 一层搞定
@Data
public class UserForm {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度2-20字符")
private String username;
@Email(message = "邮箱格式不正确")
private String email;
}
@PostMapping
public Result<Void> save(@RequestBody @Valid UserForm form) {
// 校验失败自动抛异常,GlobalExceptionHandler 统一处理
return Result.judge(userService.save(form));
}
业务校验(如用户名是否已存在)放 Service 层。
日志规范:问题排查利器
级别
- ERROR:需要立即处理
- WARN:可能有问题
- INFO:关键流程
- DEBUG:调试信息
格式
// ✅ 占位符
log.info("用户登录:userId={}, username={}", userId, username);
// ❌ 字符串拼接
log.info("用户登录:userId=" + userId);
敏感信息要脱敏,手机号、密码别直接打印。
最佳实践:避免低级 Bug
避免魔法值
// ❌
if (user.getStatus() == 1) { }
// ✅
if (Objects.equals(user.getStatus(), UserStatusEnum.ENABLED.getValue())) { }
集合判空
// ❌
if (list.size() > 0) { }
// ✅
if (CollectionUtil.isNotEmpty(list)) { }
避免 NPE
// ❌
return user.getUsername().equals("admin");
// ✅
return Objects.equals(user.getUsername(), "admin");
字符串判空
// ❌
if (str != null && str.length() > 0) { }
// ✅
if (StrUtil.isNotBlank(str)) { }
结语
规范这东西,写的时候觉得麻烦,维护的时候就知道香了。新人来了看一遍文档就能上手,review 时不用纠结命名问题,效率提升明显。
这套规范不算复杂,核心就是保持一致性。团队可以根据实际情况调整,但调整前想清楚为什么,别为了省事破坏一致性。
更多推荐




所有评论(0)