开源仓库:https://atomgit.com/youlai/youlai-boot

在这里插入图片描述

前言

本系列文章基于 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()

别用 addmodifyremove,保持一致性。看到 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 时不用纠结命名问题,效率提升明显。

这套规范不算复杂,核心就是保持一致性。团队可以根据实际情况调整,但调整前想清楚为什么,别为了省事破坏一致性。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐