Claude-skill-registry java-springboot-dev
Java Spring Boot 开发规范和最佳实践指南。适用于企业级Java后端开发,涵盖项目架构、编码规范、安全实践、性能优化等全方位内容。
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/java-springboot-dev" ~/.claude/skills/majiayu000-claude-skill-registry-java-springboot-dev && rm -rf "$T"
manifest:
skills/data/java-springboot-dev/SKILL.mdsource content
Java Spring Boot 开发规范
技术栈
本项目基于 Spring Boot 3.x + MyBatis-Plus + Spring Security + PostgreSQL + Redis
核心原则
- SOLID 原则:单一职责、开闭原则、里氏替换、接口隔离、依赖倒置
- DRY 原则:Don't Repeat Yourself,避免代码重复
- KISS 原则:Keep It Simple, Stupid,保持简单
- YAGNI 原则:You Aren't Gonna Need It,不要过度设计
- OWASP 安全最佳实践:输入验证、SQL注入防护、XSS防护、CSRF防护
- 代码覆盖率:单元测试覆盖率 ≥ 80%
项目架构
模块划分
project-root/ ├── gyl-common/ # 通用工具模块 │ ├── util/ # 工具类 │ ├── dto/ # 通用DTO │ ├── enums/ # 通用枚举 │ ├── result/ # 统一响应封装 │ ├── redis/ # Redis工具 │ └── exception/ # 异常定义 ├── gyl-core/ # 核心业务模块 │ ├── service/ # Service层 │ ├── manager/ # Manager层 │ ├── dto/ # 业务DTO │ ├── convert/ # DTO转换 │ └── enums/ # 业务枚举 ├── gyl-gateway/ # 网关层 │ └── controller/ # Controller层 ├── gyl-mapper/ # 数据持久化层 │ ├── entity/ # 实体类 │ ├── mapper/ # Mapper接口 │ └── resources/mapper/ # MyBatis XML └── doc/ # 文档目录 ├── sql/ # SQL文档 └── api/ # API文档
分层架构规范
Gateway层 (Controller)
职责:
- 参数校验和验证
- 请求路由和分发
- HTTP响应封装
- 禁止编写业务逻辑
调用规范:
- 只能调用gyl-core下的Service方法
- 禁止直接调用Manager或Mapper
- 超过2个参数必须定义DTO
API规范:
- 请求方法:仅允许POST和GET
- 路径规范:
- 后台管理:
/api/admin/ - 商城应用:
/api/app/ - 仓配系统:
/api/wms/
- 后台管理:
- 响应格式:统一使用
封装Result
示例:
@RestController @RequestMapping("/api/admin/users") public class UserController { @Autowired private UserService userService; @PostMapping("/create") public Result<UserResponseDTO> createUser(@Valid @RequestBody UserCreateDTO dto) { // 仅做参数校验和路由 UserResponseDTO response = userService.createUser(dto); return Result.success(response); } }
Service层
职责:
- 业务逻辑实现
- 业务规则校验
- 权限控制
- 事务管理
- 数据验证
调用规范:
- 必须通过Manager访问数据库和缓存
- 禁止直接操作Mapper或Redis
- 入参和返参使用DTO类型
设计原则:
- 一个业务领域一个Service
- 一个功能一个方法
- 避免Service之间相互调用
示例:
@Service public class UserService { @Autowired private UserManager userManager; public UserResponseDTO createUser(UserCreateDTO dto) { // 业务逻辑 validateUser(dto); // 通过Manager操作数据 UserEntity user = userManager.createUser(dto); // DTO转换 return UserConvert.toResponseDTO(user); } }
Manager层
职责:
- 封装数据库访问
- 封装缓存操作
- 聚合多表读写
- 提供细粒度的领域方法
调用规范:
- 仅供Service层调用
- 禁止Gateway层或其他层直接调用
- 只能调用Mapper接口进行数据库操作
- 统一使用RedisUtil操作Redis
设计原则:
- 一个领域聚合一个Manager
- 可组合调用其他Manager,但应避免循环依赖
- 跨表写操作必须使用
@Transactional(rollbackFor = Exception.class)
数据类型:
- 入参:Entity、自定义DO对象、DTO
- 返回:Entity、DTO、DO对象
- 禁止使用多个变量作为参数
错误示例(禁止):
// 错误:参数过多 boolean deductPoints(Integer shopId, Integer userId, Integer points, Integer changeType, String businessType, String businessId, String remark, Integer operatorId, String operatorName);
正确示例:
// 正确:使用DTO boolean deductPoints(PointDeductDTO dto);
Entity赋值规范:
- 创建或更新Entity时,必须为所有必填字段逐一显式赋值
- 严禁使用
BeanUtil.copyProperties() - 建议使用Builder模式或全参构造函数
Mapper层
职责:
- 数据库访问
- 通用的增删改查
- 分页查询
- 多表联合查询(不超过3个表)
技术选型:
- 优先使用MyBatis-Plus单表CRUD
- 复杂查询使用XML
- 禁止在Mapper方法中写SQL
接口规范:
- 必须继承
com.baomidou.mybatisplus.core.mapper.BaseMapper<Entity> - XML文件命名为
表名+Mapper.xml - 存放在
gyl-mapper/src/main/resources/mapper/
数据类型:
- 入参:Entity、自定义DO对象
- 返回:Entity(整表字段)、DO对象(多表联合字段)
- 禁止自定义Map类型
示例:
// Mapper接口 @Mapper public interface UserMapper extends BaseMapper<UserEntity> { // 单表查询使用MyBatis-Plus List<UserEntity> selectByCondition(LambdaQueryWrapper<UserEntity> wrapper); // 多表查询在XML中实现 UserDetailDO selectUserDetailById(Long userId); }
<!-- UserMapper.xml --> <mapper namespace="com.gyl.mapper.UserMapper"> <select id="selectUserDetailById" resultType="com.gyl.mapper.DO.UserDetailDO"> SELECT u.*, p.phone FROM user u LEFT JOIN phone p ON u.id = p.user_id WHERE u.id = #{userId} </select> </mapper>
通用工具使用规范
分页处理
- 所有分页DTO必须继承
PageQueryDTO - 单表查询使用
MyBatisPlusUtils - 分页返回对象使用
ListWithPageDTO
// 分页查询DTO public class UserPageQueryDTO extends PageQueryDTO { private String username; private Integer status; } // 分页返回DTO public class UserPageResponseDTO { private List<UserResponseDTO> list; private Long total; } // 使用 public ListWithPageDTO<UserResponseDTO> queryUserPage(UserPageQueryDTO dto) { return MyBatisPlusUtils.page( dto, UserEntity.class, wrapper -> wrapper.like(UserEntity::getUsername, dto.getUsername()) ); }
金额处理
- 所有金额转换统一使用
AmountUtil - 数据库存储:分为单位(Integer)
- 前端展示:元为单位(BigDecimal)
- 严禁使用float和double进行金额计算
// 前端传入:元(BigDecimal) BigDecimal amount = new BigDecimal("99.99"); // 转为分存入数据库 Integer fen = AmountUtil.yuanToFen(amount); // 9999 // 从数据库读取:分 Integer fen = 9999; // 转为元返回给前端 BigDecimal yuan = AmountUtil.fenToYuan(fen); // 99.99
用户信息获取
- 获取当前登录用户必须使用
GatewayContextHolder - 只能在Controller层获取
- Service层和Manager层可以直接使用
// Controller层获取 @GetMapping("/current") public Result<UserResponseDTO> getCurrentUser() { Long userId = GatewayContextHolder.getUserId(); String username = GatewayContextHolder.getUsername(); UserResponseDTO user = userService.getUserById(userId); return Result.success(user); } // Service层直接使用 public UserResponseDTO getUserById(Long userId) { // 可以直接调用GatewayContextHolder String currentUsername = GatewayContextHolder.getUsername(); logger.info("当前用户:{} 正在查询用户:{}", currentUsername, userId); return userManager.getUserById(userId); }
Redis缓存
- Redis操作统一使用
工具类RedisUtil - 所有Redis key维护在
中RedisKeyConsts - 缓存命名规范:
模块名_功能名_标识符
// Redis Key定义 public class RedisKeyConsts { public static final String USER_INFO = "user:info:%s"; public static final String USER_TOKEN = "user:token:%s"; } // 使用Redis public UserEntity getUserById(Long userId) { String key = String.format(RedisKeyConsts.USER_INFO, userId); // 先查缓存 UserEntity user = redisUtil.get(key, UserEntity.class); if (user != null) { return user; } // 查数据库 user = userMapper.selectById(userId); // 写缓存(7天过期) redisUtil.set(key, user, 60 * 60 * 24 * 7); return user; }
Hutool工具库
// 字符串操作 StrUtil.isEmpty(str); StrUtil.isNotBlank(str); // 集合操作 CollUtil.isEmpty(list); CollUtil.isNotEmpty(list); // 时间处理 DateUtil.now(); DateUtil.format(date, "yyyy-MM-dd HH:mm:ss"); DateUtil.parse("2025-01-16", "yyyy-MM-dd"); // UUID生成 String uuid = IdUtil.fastUUID();
数据处理规范
DTO规范
命名规范:
- 功能明确命名:
、AdminUserCreateDTOMallUserLoginDTO
字段校验:
- 使用Bean Validation注解
、@NotBlank
、@Size
、@Email
等@Pattern
分类存放:
- 按功能分包存放:
、user/
、shop/
等goods/
对象转换:
- 禁止使用
BeanUtil.copyProperties() - 采用手动字段赋值
- DTO转换方法统一放在各模块convert包中
// DTO定义 public class UserCreateDTO { @NotBlank(message = "用户名不能为空") @Size(min = 3, max = 20, message = "用户名长度为3-20字符") private String username; @NotBlank(message = "密码不能为空") @Size(min = 6, max = 20, message = "密码长度为6-20字符") private String password; @Email(message = "邮箱格式不正确") @NotBlank(message = "邮箱不能为空") private String email; } // Convert类 public class UserConvert { public static UserResponseDTO toResponseDTO(UserEntity entity) { if (entity == null) { return null; } UserResponseDTO dto = new UserResponseDTO(); dto.setId(entity.getId()); dto.setUsername(entity.getUsername()); dto.setEmail(entity.getEmail()); dto.setCreateTime(entity.getCreateTime()); // 手动赋值所有字段 return dto; } public static UserEntity toEntity(UserCreateDTO dto) { UserEntity entity = new UserEntity(); entity.setUsername(dto.getUsername()); entity.setPassword(MD5.create().digestHex(dto.getPassword())); entity.setEmail(dto.getEmail()); entity.setStatus(UserStatusEnum.ACTIVE.getCode()); // 手动赋值所有字段 return entity; } }
枚举和状态管理
- 所有状态字段使用枚举类型
- 避免魔法数字
- 枚举定义在对应模块的enums包中
- 状态变更通过枚举方法进行校验
public enum UserStatusEnum { ACTIVE(1, "正常"), INACTIVE(0, "禁用"), DELETED(-1, "已删除"); private final Integer code; private final String desc; UserStatusEnum(Integer code, String desc) { this.code = code; this.desc = desc; } public Integer getCode() { return code; } public String getDesc() { return desc; } public static UserStatusEnum fromCode(Integer code) { for (UserStatusEnum status : values()) { if (status.getCode().equals(code)) { return status; } } throw new BusinessException("无效的用户状态"); } // 状态转换校验 public boolean canTransitionTo(UserStatusEnum target) { if (this == DELETED) { return false; // 已删除不能转换 } return true; } }
安全规范
密码安全
// 密码加密(MD5) String encryptedPassword = MD5.create().digestHex(rawPassword); // 密码验证 boolean matches = MD5.create().digestHex(inputPassword) .equals(storedPassword); // 密码重置 String newPassword = generateRandomPassword(); String encryptedNewPassword = MD5.create().digestHex(newPassword);
数据验证
- 所有外部输入必须验证
- 使用参数化查询防SQL注入
- 对输出内容进行转义防XSS
// Controller参数验证 @PostMapping("/create") public Result<Void> createUser(@Valid @RequestBody UserCreateDTO dto) { // Spring会自动验证@Valid注解的DTO userService.createUser(dto); return Result.success(); } // SQL参数化查询(MyBatis-Plus自动支持) LambdaQueryWrapper<UserEntity> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(UserEntity::getUsername, username); // 自动参数化
异常处理
异常分类:
: 业务逻辑异常BusinessException
: 服务层处理异常ServiceException
: 数据访问异常DataAccessException
异常处理原则:
- 所有异常catch必须记录详细日志
- Service层异常由统一异常处理器处理
- 事务异常:在
函数中不要使用@Transactional
捕获异常try...catch
// 全局异常处理器 @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public Result<Void> handleBusinessException(BusinessException e) { logger.error("业务异常:{}", e.getMessage(), e); return Result.fail(e.getCode(), e.getMessage()); } @ExceptionHandler(Exception.class) public Result<Void> handleException(Exception e) { logger.error("系统异常:{}", e.getMessage(), e); return Result.fail(500, "系统异常,请联系管理员"); } } // Service层 @Transactional(rollbackFor = Exception.class) public void updateUser(UserUpdateDTO dto) { // 不要使用try...catch捕获异常 // 让异常抛出,由事务管理器处理 userManager.updateUser(dto); }
性能优化规范
数据库操作
避免循环查询:
// 错误示例 List<Integer> skuIds = Arrays.asList(1, 2, 3, 4, 5); for (Integer skuId : skuIds) { List<GylSsu> ssuList = gylSsuManager.getSsuListBySkuId(skuId); ssuListMap.put(skuId, ssuList); } // 正确示例:批量查询 List<GylSsu> ssuList = gylSsuManager.getSsuListBySkuIds(skuIds);
批量操作:
// 批量插入 void batchInsert(List<UserEntity> users) { users.forEach(user -> userMapper.insert(user)); } // 批量更新 void batchUpdate(List<UserEntity> users) { users.forEach(user -> userMapper.updateById(user)); }
缓存策略
- 避免在事务中进行Redis操作
- 合理设置缓存过期时间
- 及时清理无效缓存
// 缓存更新 @Transactional(rollbackFor = Exception.class) public void updateUser(UserUpdateDTO dto) { // 1. 先更新数据库 userManager.updateUser(dto); // 2. 事务提交后再更新缓存 TransactionSynchronizationManager.registerSynchronization( new TransactionSynchronization() { @Override public void afterCommit() { // 更新缓存 String key = String.format(RedisKeyConsts.USER_INFO, dto.getId()); redisUtil.del(key); } } ); }
内存优化
- 及时释放大对象引用
- 避免创建不必要的临时对象
严格禁止事项
- 文件修改:禁止未经允许修改
和实体类文件pom.xml - 对象拷贝:禁止使用
BeanUtil.copyProperties() - 循环查询:禁止在循环中查询数据
- 包名调用:不要使用完整包名调用类或方法
- 数据库变更:必须生成SQL文档存放在
目录doc/sql/ - 空方法:禁止生成空方法或使用测试/默认值
- 直接SQL:所有SQL必须写到mapper.xml文件中
开发流程
标准开发流程
- 需求分析:查阅 README 确认是否有相似功能
- 架构设计:确定核心逻辑实现层级
- Mapper层:定义数据访问接口
- Service层:实现具体业务流程
- Controller层:提供HTTP接口
代码复用原则
- 开发前必查 README 中的service说明
- 发现重复代码时,及时重构到合适层级
代码质量要求
- 每个方法添加Javadoc注释
- 方法行数不超过300行
- 使用描述性的变量、函数和类名
- 保持代码简洁明了,遵循KISS原则
最佳实践总结
关注点分离
- Gateway层专注请求处理
- Service层专注业务逻辑
- Mapper层专注数据访问
可维护性
- 使用枚举管理状态
- 使用DTO传输数据
- 手动字段赋值提高可读性
- 完善的异常处理和日志记录
性能优化
- 批量操作代替逐个操作
- 合理使用缓存
- 避免N+1查询问题