在 Spring Boot 项目中写出“优雅的 Service 层”,核心在于:
职责清晰、逻辑清楚、事务管理得当、异常处理规范、方法命名明确、代码可测试性强、可维护性好
✅ 一、Service 层的职责是什么?
封装业务逻辑、组合调用 DAO 层、处理异常、控制事务,不处理请求参数、不处理数据库细节、不返回 HTTP 响应结构。
✅ 二、优雅 Service 的六大核心规范
1️⃣ 清晰的命名与分层
示例结构:
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
@Override
public UserDto getUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new BizException("用户不存在"));
return UserMapper.INSTANCE.toDto(user);
}
}
关键点:
层 | 负责内容 | 命名建议 |
---|---|---|
Controller | 处理 HTTP 请求 | UserController |
Service | 业务逻辑 | UserService , UserServiceImpl |
Repository | 数据访问 | UserRepository (JPA)/ UserMapper (MyBatis) |
2️⃣ 使用 DTO/VO 避免直接暴露 Entity
// Entity(数据库表)
@Data
public class User {
private Long id;
private String username;
private String password;
}
// DTO(前端返回用)
@Data
public class UserDto {
private Long id;
private String username;
}
配合 MapStruct 自动转换:
@Mapper(componentModel = "spring")
public interface UserMapper {
UserDto toDto(User user);
User toEntity(UserCreateRequest request);
}
3️⃣ 事务控制明确(@Transactional)
- 读写分离:写操作方法加上
@Transactional
- 防止回滚失败:抛出运行时异常才能触发事务回滚
示例:
@Transactional
public void createUser(UserCreateRequest request) {
User user = userMapper.toEntity(request);
userRepository.save(user);
}
4️⃣ 优雅处理异常
自定义业务异常:
public class BizException extends RuntimeException {
public BizException(String message) {
super(message);
}
}
使用:
userRepository.findById(id).orElseThrow(() -> new BizException("用户不存在"));
统一在 @ControllerAdvice
中捕获处理。
5️⃣ 方法粒度控制
- 方法不要过长(推荐每个方法控制在 20~30 行内)
- 一个方法只做一件事(单一职责原则)
- 大逻辑拆分为多个私有方法组合
✅ 优雅写法示例:
@Transactional
public void registerUser(UserRegisterRequest req) {
checkDuplicate(req.getEmail());
User user = buildUser(req);
userRepository.save(user);
sendWelcomeEmail(user);
}
private void checkDuplicate(String email) {
if (userRepository.existsByEmail(email)) {
throw new BizException("邮箱已注册");
}
}
6️⃣ 可测试性强
- Service 层应无依赖于 Web/Servlet
- 方法返回值可预测,便于单元测试
单元测试示例:
@SpringBootTest
class UserServiceTest {
@Autowired
private UserService userService;
@Test
void testGetUserById() {
UserDto dto = userService.getUserById(1L);
assertEquals("张三", dto.getUsername());
}
}
✅ 三、代码风格推荐
建议 | 示例 |
---|---|
方法名语义清晰 | getUserById , registerUser |
使用 Optional 避免 null |
Optional<User> findByEmail() |
依赖注入使用 @RequiredArgsConstructor |
减少样板代码 |
拆分重复逻辑为私有方法 | 提高复用性与可测试性 |
多参数封装为 Request 对象 | UserCreateRequest |
✅ 四、常用工具类
MapStruct
:Entity/DTO 相互转换BeanUtils.copyProperties()
:简易字段拷贝PageHelper
/PageRequest
:分页参数封装Validator
:自定义参数校验
✅ 五、服务返回结构统一建议
Service 方法尽量直接返回业务对象或布尔/主键等简单结构:
public UserDto getUserById(Long id);
public boolean updatePassword(Long id, String newPassword);
public Long createUser(UserCreateRequest request);
Controller 中再统一封装为 R<T>
返回。
✅ 六、总结口诀
“Controller 只转发,Service 做判断,Entity 不暴露,事务别忘了,异常要抛清”