编码规范¶
基于《阿里巴巴Java开发手册》,结合项目实际情况制定
📋 目录¶
📝 命名规范¶
基本原则¶
- 见名知义:命名要能准确表达其含义
- 简洁明了:在表达清楚的前提下尽量简短
- 统一风格:项目内保持一致的命名风格
包名规范¶
cn.zhangziming.auth.{模块名}.{层级}
示例:
cn.zhangziming.auth.common.exception
cn.zhangziming.auth.security.jwt
cn.zhangziming.auth.server.controller
规则:
- ✅ 全部小写
- ✅ 单词间用点号分隔
- ✅ 不使用下划线或大写字母
- ❌ cn.zhangziming.Boot.Common (错误:使用了大写)
- ❌ cn.zhangziming.auth_common (错误:使用了下划线)
类名规范¶
普通类¶
// ✅ 正确示例
public class UserController { }
public class OrderService { }
public class ProductMapper { }
// ❌ 错误示例
public class usercontroller { } // 应使用大驼峰
public class User_Service { } // 不要使用下划线
特殊类命名¶
| 类型 | 规范 | 示例 |
|---|---|---|
| Controller | XxxController |
UserController |
| Service接口 | IXxxService 或 XxxService |
IUserService |
| Service实现 | XxxServiceImpl |
UserServiceImpl |
| Mapper/DAO | XxxMapper |
UserMapper |
| Entity/DO | Xxx |
User, Role |
| DTO | XxxDTO |
UserDTO, LoginDTO |
| VO | XxxVO |
UserVO |
| Request | XxxRequest |
LoginRequest |
| Response | XxxResponse |
TokenResponse |
| Exception | XxxException |
BusinessException |
| Utils | XxxUtil 或 XxxUtils |
StringUtil |
| Config | XxxConfig |
RedisConfig |
| Constant | XxxConstant |
CacheConstant |
方法名规范¶
// ✅ 正确示例
public void saveUser() { } // 保存
public void deleteUser() { } // 删除
public void updateUser() { } // 更新
public User getUserById(Long id) { } // 查询单个
public List<User> listUsers() { } // 查询列表
public boolean isAdmin() { } // 判断
public boolean hasPermission() { } // 判断是否拥有
public void validateToken() { } // 验证
public String convertToJson() { } // 转换
// ❌ 错误示例
public void SaveUser() { } // 首字母不应大写
public void get_user() { } // 不应使用下划线
public void user() { } // 不清楚具体操作
变量名规范¶
// ✅ 正确示例
private String username;
private Long userId;
private List<User> userList;
private Map<String, Object> dataMap;
private boolean isDeleted;
private boolean hasPermission;
// ❌ 错误示例
private String UserName; // 应使用小驼峰
private Long user_id; // 不应使用下划线
private List<User> list; // 命名太泛化
private boolean deleted; // boolean应以is/has等开头
常量名规范¶
// ✅ 正确示例
public static final String DEFAULT_CHARSET = "UTF-8";
public static final int MAX_PAGE_SIZE = 100;
public static final long TOKEN_EXPIRE_TIME = 7200L;
// ❌ 错误示例
public static final String defaultCharset = "UTF-8"; // 应全大写
public static final int maxPageSize = 100; // 应使用下划线分隔
枚举类命名¶
// ✅ 正确示例
public enum UserStatus {
NORMAL(1, "正常"),
DISABLED(0, "禁用"),
LOCKED(2, "锁定");
private final Integer code;
private final String desc;
// getter...
}
public enum LoginType {
PASSWORD, // 密码登录
SMS, // 短信登录
WECHAT // 微信登录
}
🎨 代码格式¶
缩进¶
- 使用 4个空格 缩进,不使用Tab
- IDE配置:Tab转换为4个空格
// ✅ 正确
public class UserService {
public void saveUser() {
if (user != null) {
userMapper.insert(user);
}
}
}
// ❌ 错误(缩进不一致)
public class UserService {
public void saveUser() {
if (user != null) {
userMapper.insert(user);
}
}
}
单行长度¶
- 单行字符数不超过 120个字符
- 超过需要换行,遵循以下原则:
// ✅ 正确:在逗号后换行
String message = String.format("用户[%s]执行了操作[%s],"
+ "结果为[%s],耗时[%d]ms",
username, operation, result, time);
// ✅ 正确:方法参数过多时换行
public void updateUser(Long userId,
String username,
String email,
String phone) {
// ...
}
// ✅ 正确:链式调用换行
List<String> names = userList.stream()
.filter(user -> user.getStatus() == 1)
.map(User::getUsername)
.collect(Collectors.toList());
空格规范¶
// ✅ 正确:关键字与括号之间要有空格
if (condition) { }
for (int i = 0; i < 10; i++) { }
while (condition) { }
// ✅ 正确:运算符左右要有空格
int result = a + b;
boolean flag = (a > b) && (c < d);
// ✅ 正确:逗号、分号后要有空格
method(arg1, arg2, arg3);
// ❌ 错误:缺少空格
if(condition){ }
int result=a+b;
method(arg1,arg2,arg3);
空行规范¶
public class UserService {
private UserMapper userMapper;
/**
* 保存用户
*/
public void saveUser(User user) {
validateUser(user);
userMapper.insert(user);
sendNotification(user);
}
/**
* 验证用户
*/
private void validateUser(User user) {
// ...
}
}
规则: - 方法之间空一行 - 逻辑块之间空一行 - 成员变量和方法之间空一行
大括号规范¶
// ✅ 正确:K&R风格(推荐)
if (condition) {
doSomething();
} else {
doOtherThing();
}
public void method() {
// ...
}
// ❌ 错误:不推荐的风格
if (condition)
{
doSomething();
}
// ❌ 错误:单行也要加大括号
if (condition) doSomething(); // 不推荐
💬 注释规范¶
类注释¶
/**
* 用户服务类
*
* <p>提供用户的增删改查等基础功能
* <p>包含用户密码加密、权限验证等逻辑
*
* @author zhangziming
* @since 1.0.0
*/
public class UserService {
// ...
}
方法注释¶
/**
* 保存用户信息
*
* <p>保存前会进行以下验证:
* <ul>
* <li>用户名不能为空</li>
* <li>用户名不能重复</li>
* <li>密码会自动加密</li>
* </ul>
*
* @param user 用户对象,不能为null
* @return 保存后的用户ID
* @throws BusinessException 当用户名重复时抛出
*/
public Long saveUser(User user) {
// ...
}
字段注释¶
public class User {
/**
* 用户ID
*/
private Long id;
/**
* 用户名(唯一)
*/
private String username;
/**
* 密码(BCrypt加密存储)
*/
private String password;
/**
* 用户状态
* 0-禁用 1-正常 2-锁定
*/
private Integer status;
}
代码注释¶
public void processOrder(Order order) {
// 1. 验证订单
validateOrder(order);
// 2. 计算价格
BigDecimal totalPrice = calculatePrice(order);
// 3. 扣减库存
reduceStock(order);
// 4. 创建订单
orderMapper.insert(order);
// TODO: 发送订单通知
// FIXME: 需要增加事务处理
}
特殊标记¶
| 标记 | 含义 | 使用场景 |
|---|---|---|
TODO |
待办事项 | 需要实现但暂时未实现的功能 |
FIXME |
需要修复 | 已知的Bug或问题 |
XXX |
需要注意 | 存在风险或需要特别注意的地方 |
@deprecated |
已废弃 | 不推荐使用的方法或类 |
// TODO: [zhangziming] 2024-10-28 需要增加缓存逻辑
public User getUser(Long id) {
return userMapper.selectById(id);
}
// FIXME: [zhangziming] 2024-10-28 并发情况下可能有问题
public void updateUserCount() {
int count = getCount();
setCount(count + 1);
}
/**
* @deprecated 使用 {@link #saveUser(UserDTO)} 代替
*/
@Deprecated
public void save(User user) {
// ...
}
📐 编程规约¶
常量定义¶
// ✅ 正确:定义在常量类中
public class CacheConstant {
/** 用户缓存前缀 */
public static final String USER_CACHE_PREFIX = "user:";
/** 用户缓存过期时间(秒) */
public static final long USER_CACHE_EXPIRE = 1800L;
}
// ❌ 错误:魔法值
userCache.set("user:" + userId, user, 1800);
// ✅ 正确:使用常量
userCache.set(USER_CACHE_PREFIX + userId, user, USER_CACHE_EXPIRE);
集合处理¶
// ✅ 正确:使用isEmpty判断
if (CollectionUtils.isEmpty(list)) {
// ...
}
// ❌ 错误:使用size() == 0判断(性能差)
if (list.size() == 0) {
// ...
}
// ✅ 正确:使用ArrayList初始化时指定大小
List<User> userList = new ArrayList<>(100);
// ✅ 正确:使用Arrays.asList初始化
List<String> list = Arrays.asList("a", "b", "c");
// ❌ 错误:Arrays.asList的列表不能增删
list.add("d"); // 运行时异常
// ✅ 正确:需要增删时用new ArrayList包装
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
list.add("d"); // OK
字符串处理¶
// ✅ 正确:使用equals判断相等
if ("admin".equals(username)) {
// ...
}
// ❌ 错误:可能空指针
if (username.equals("admin")) {
// ...
}
// ✅ 正确:字符串拼接使用StringBuilder
StringBuilder sb = new StringBuilder();
for (String item : list) {
sb.append(item).append(",");
}
// ❌ 错误:循环中使用+拼接(性能差)
String result = "";
for (String item : list) {
result += item + ",";
}
// ✅ 正确:使用StringUtils工具类
if (StringUtils.isNotBlank(username)) {
// ...
}
条件判断¶
// ✅ 正确:避免复杂的条件判断
boolean isValidUser = user != null
&& StringUtils.isNotBlank(user.getUsername())
&& user.getStatus() == 1;
if (isValidUser) {
// ...
}
// ✅ 正确:提前返回,减少嵌套
public void process(User user) {
if (user == null) {
return;
}
if (StringUtils.isBlank(user.getUsername())) {
return;
}
// 正常处理逻辑
doProcess(user);
}
// ❌ 错误:多层嵌套
public void process(User user) {
if (user != null) {
if (StringUtils.isNotBlank(user.getUsername())) {
if (user.getStatus() == 1) {
// 正常处理逻辑
}
}
}
}
空值处理¶
// ✅ 正确:返回空集合而不是null
public List<User> listUsers() {
List<User> users = userMapper.selectList();
return users != null ? users : Collections.emptyList();
}
// ❌ 错误:返回null
public List<User> listUsers() {
return userMapper.selectList(); // 可能为null
}
// ✅ 正确:使用Optional
public Optional<User> getUser(Long id) {
return Optional.ofNullable(userMapper.selectById(id));
}
// 使用
userService.getUser(1L).ifPresent(user -> {
// 处理用户
});
对象比较¶
// ✅ 正确:包装类使用equals
Long userId1 = 1L;
Long userId2 = 1L;
if (userId1.equals(userId2)) {
// ...
}
// ❌ 错误:包装类使用 ==
if (userId1 == userId2) { // 可能不相等
// ...
}
// ✅ 正确:枚举使用 == 或 equals都可以
if (UserStatus.NORMAL == user.getStatus()) {
// ...
}
⚠️ 异常处理¶
异常分类¶
/**
* 业务异常
* 可预期的异常,需要给用户友好提示
*/
public class BusinessException extends RuntimeException {
private Integer code;
public BusinessException(String message) {
super(message);
this.code = 400;
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
}
/**
* 系统异常
* 不可预期的异常,记录日志并给用户通用提示
*/
public class SystemException extends RuntimeException {
public SystemException(String message) {
super(message);
}
public SystemException(String message, Throwable cause) {
super(message, cause);
}
}
异常使用规范¶
// ✅ 正确:抛出具体的异常
public User getUser(Long userId) {
if (userId == null) {
throw new BusinessException("用户ID不能为空");
}
User user = userMapper.selectById(userId);
if (user == null) {
throw new BusinessException("用户不存在");
}
return user;
}
// ❌ 错误:吞掉异常
try {
doSomething();
} catch (Exception e) {
// 什么都不做
}
// ✅ 正确:记录日志或重新抛出
try {
doSomething();
} catch (Exception e) {
log.error("操作失败", e);
throw new SystemException("系统异常,请稍后重试", e);
}
// ❌ 错误:捕获Exception
try {
doSomething();
} catch (Exception e) {
// ...
}
// ✅ 正确:捕获具体异常
try {
doSomething();
} catch (IOException e) {
// 处理IO异常
} catch (SQLException e) {
// 处理SQL异常
}
全局异常处理¶
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
/**
* 业务异常
*/
@ExceptionHandler(BusinessException.class)
public Result<?> handleBusinessException(BusinessException e) {
log.warn("业务异常: {}", e.getMessage());
return Result.error(e.getCode(), e.getMessage());
}
/**
* 参数校验异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<?> handleValidationException(MethodArgumentNotValidException e) {
String message = e.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.collect(Collectors.joining(", "));
return Result.error(400, message);
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public Result<?> handleException(Exception e) {
log.error("系统异常", e);
return Result.error(500, "系统异常,请稍后重试");
}
}
📋 日志规范¶
日志级别¶
| 级别 | 使用场景 | 示例 |
|---|---|---|
| ERROR | 错误信息 | 系统异常、业务异常 |
| WARN | 警告信息 | 参数不合法、降级处理 |
| INFO | 重要信息 | 启动信息、关键业务操作 |
| DEBUG | 调试信息 | 开发调试 |
| TRACE | 跟踪信息 | 详细的调试信息 |
日志使用规范¶
@Slf4j
public class UserService {
public void saveUser(User user) {
// ✅ 正确:使用占位符
log.info("保存用户,username={}, email={}",
user.getUsername(), user.getEmail());
// ❌ 错误:使用字符串拼接(即使不输出也会执行拼接)
log.info("保存用户,username=" + user.getUsername());
// ✅ 正确:异常日志包含堆栈
try {
userMapper.insert(user);
} catch (Exception e) {
log.error("保存用户失败,userId={}", user.getId(), e);
}
// ✅ 正确:敏感信息脱敏
log.info("用户登录,username={}, password={}",
user.getUsername(), "******");
}
}
日志格式¶
# logback-spring.xml
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
输出示例:
2024-10-28 10:00:00.123 [http-nio-8080-exec-1] INFO c.z.b.s.UserService - 保存用户,username=admin, email=admin@example.com
🗄️ 数据库规范¶
表命名规范¶
- 表名全部小写,单词间用下划线分隔
- 表名使用复数形式或业务含义明确的名词
- 不要使用MySQL保留字
-- ✅ 正确
CREATE TABLE sys_user (...);
CREATE TABLE sys_role (...);
CREATE TABLE sys_user_role (...);
-- ❌ 错误
CREATE TABLE User (...); -- 不要使用大写
CREATE TABLE user (...); -- MySQL保留字
CREATE TABLE sys-user (...); -- 不要使用连字符
字段命名规范¶
CREATE TABLE sys_user (
-- ✅ 正确:主键使用id
id BIGINT PRIMARY KEY AUTO_INCREMENT,
-- ✅ 正确:字段名全小写,下划线分隔
username VARCHAR(64) NOT NULL,
password VARCHAR(128) NOT NULL,
create_time DATETIME NOT NULL,
update_time DATETIME NOT NULL,
-- ✅ 正确:布尔字段使用is_前缀
is_deleted TINYINT DEFAULT 0,
is_enabled TINYINT DEFAULT 1
);
-- ❌ 错误示例
CREATE TABLE sys_user (
userId BIGINT, -- 不要使用驼峰
user_name VARCHAR(64), -- username更简洁
createTime DATETIME -- 不要使用驼峰
);
索引规范¶
-- ✅ 正确:索引命名规范
-- 唯一索引:uk_字段名
CREATE UNIQUE INDEX uk_username ON sys_user(username);
-- 普通索引:idx_字段名
CREATE INDEX idx_create_time ON sys_user(create_time);
-- 联合索引:idx_字段1_字段2
CREATE INDEX idx_username_status ON sys_user(username, status);
-- ❌ 错误:索引名不规范
CREATE INDEX index_1 ON sys_user(username);
CREATE INDEX user_index ON sys_user(username);
SQL编写规范¶
// ✅ 正确:使用预编译SQL
@Select("SELECT * FROM sys_user WHERE username = #{username}")
User selectByUsername(@Param("username") String username);
// ❌ 错误:SQL拼接(有注入风险)
String sql = "SELECT * FROM sys_user WHERE username = '" + username + "'";
// ✅ 正确:分页查询
@Select("SELECT * FROM sys_user LIMIT #{offset}, #{limit}")
List<User> selectByPage(@Param("offset") int offset,
@Param("limit") int limit);
// ✅ 正确:批量插入
@Insert("<script>" +
"INSERT INTO sys_user (username, password) VALUES " +
"<foreach collection='list' item='item' separator=','>" +
"(#{item.username}, #{item.password})" +
"</foreach>" +
"</script>")
int batchInsert(@Param("list") List<User> users);
🔒 安全规范¶
输入验证¶
// ✅ 正确:使用Validation注解
public class UserDTO {
@NotBlank(message = "用户名不能为空")
@Length(min = 3, max = 20, message = "用户名长度为3-20个字符")
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
private String username;
@NotBlank(message = "密码不能为空")
@Length(min = 6, max = 20, message = "密码长度为6-20个字符")
private String password;
@Email(message = "邮箱格式不正确")
private String email;
}
@PostMapping("/register")
public Result<Void> register(@Valid @RequestBody UserDTO userDTO) {
// @Valid会自动验证
userService.register(userDTO);
return Result.success();
}
密码处理¶
// ✅ 正确:使用BCrypt加密
@Service
public class UserService {
@Autowired
private PasswordEncoder passwordEncoder;
public void saveUser(User user) {
// 加密密码
String encryptedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(encryptedPassword);
userMapper.insert(user);
}
public boolean validatePassword(String rawPassword, String encodedPassword) {
return passwordEncoder.matches(rawPassword, encodedPassword);
}
}
// ❌ 错误:明文存储密码
user.setPassword(rawPassword);
userMapper.insert(user);
// ❌ 错误:使用MD5加密(不安全)
String md5Password = DigestUtils.md5Hex(password);
SQL注入防护¶
// ✅ 正确:使用参数化查询
@Select("SELECT * FROM sys_user WHERE username = #{username}")
User selectByUsername(String username);
// ✅ 正确:MyBatis Plus
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", username);
userMapper.selectOne(wrapper);
// ❌ 错误:字符串拼接(有注入风险)
@Select("SELECT * FROM sys_user WHERE username = '${username}'")
User selectByUsername(String username);
XSS防护¶
// ✅ 正确:对输出进行HTML转义
@GetMapping("/user/{id}")
public String getUserInfo(@PathVariable Long id, Model model) {
User user = userService.getUser(id);
// Thymeleaf会自动转义
model.addAttribute("username", user.getUsername());
return "user-info";
}
// 在前端使用th:text(自动转义)
// <span th:text="${username}"></span>
// ❌ 错误:不转义输出(有XSS风险)
// <span th:utext="${username}"></span>
🧪 单元测试¶
测试类命名¶
// ✅ 正确:测试类以Test结尾
public class UserServiceTest {
// ...
}
// ✅ 正确:测试方法命名清晰
@Test
public void testSaveUser_Success() {
// ...
}
@Test
public void testSaveUser_WhenUsernameExists_ShouldThrowException() {
// ...
}
测试规范¶
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService;
@MockBean
private UserMapper userMapper;
/**
* 测试保存用户 - 成功场景
*/
@Test
public void testSaveUser_Success() {
// Given (准备测试数据)
User user = new User();
user.setUsername("test");
user.setPassword("123456");
when(userMapper.insert(any())).thenReturn(1);
// When (执行测试)
Long userId = userService.saveUser(user);
// Then (验证结果)
assertNotNull(userId);
verify(userMapper, times(1)).insert(any());
}
/**
* 测试保存用户 - 用户名已存在
*/
@Test
public void testSaveUser_WhenUsernameExists_ShouldThrowException() {
// Given
User user = new User();
user.setUsername("admin");
when(userMapper.selectByUsername("admin"))
.thenReturn(new User());
// When & Then
assertThrows(BusinessException.class, () -> {
userService.saveUser(user);
});
}
}
测试覆盖率¶
- 单元测试覆盖率目标:80%以上
- 核心业务逻辑覆盖率:100%
🛠️ 工具配置¶
IDEA配置¶
1. 代码格式化¶
Settings → Editor → Code Style → Java
- Tab size: 4
- Indent: 4
- Continuation indent: 8
- 勾选 "Use tab character" 取消
2. 自动导入优化¶
Settings → Editor → General → Auto Import
- 勾选 "Add unambiguous imports on the fly"
- 勾选 "Optimize imports on the fly"
3. 保存时格式化¶
Settings → Tools → Actions on Save
- 勾选 "Reformat code"
- 勾选 "Optimize imports"
Maven插件¶
<build>
<plugins>
<!-- Checkstyle代码规范检查 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<configLocation>checkstyle.xml</configLocation>
</configuration>
</plugin>
<!-- PMD代码质量检查 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>3.21.0</version>
</plugin>
<!-- JaCoCo测试覆盖率 -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Git Hooks¶
创建 .git/hooks/pre-commit:
#!/bin/bash
# 格式化检查
mvn checkstyle:check
if [ $? -ne 0 ]; then
echo "代码格式检查失败,请修复后再提交"
exit 1
fi
# 单元测试
mvn test
if [ $? -ne 0 ]; then
echo "单元测试失败,请修复后再提交"
exit 1
fi
📚 参考资料¶
✅ 检查清单¶
提交代码前请确认:
- 代码符合命名规范
- 代码格式正确(缩进、空格、换行)
- 添加了必要的注释
- 没有硬编码(使用常量)
- 异常处理正确
- 日志使用规范
- 通过单元测试
- 测试覆盖率达标
- 没有编译警告
- 代码已格式化
遵守规范,编写优雅的代码! 💎
本文档持续更新,最后更新时间: 2024-10-28