学习目标
掌握业务逻辑层的定位、核心职责及其在系统架构中的作用
1
什么是业务逻辑层?
业务逻辑层(Service层)是分层架构中的核心组件,负责:
- 封装业务规则和流程
- 处理复杂的业务逻辑
- 协调多个数据访问对象(DAO/Repository)的操作
- 提供事务管理支持
- 处理业务异常
2
Service层的核心职责
- 实现业务规则和验证
- 协调多个Repository操作
- 管理事务边界
- 提供业务逻辑的抽象接口
- 实现领域逻辑
- 处理业务异常和错误
架构师提示
良好的Service层设计应该遵循"单一职责原则",每个Service类应该聚焦于一个特定的业务领域,避免创建"上帝对象"。同时,Service层应该保持对持久化技术的透明性,不直接依赖具体的数据库实现。
学习目标
掌握典型的分层架构设计,理解各层之间的依赖关系
分层架构中的依赖关系
- Controller层:接收HTTP请求,调用Service方法,返回响应
- Service层:实现业务逻辑,调用多个Repository,管理事务
- Repository层:提供数据访问接口,执行CRUD操作
- Model层:包含实体类、DTO、VO等数据对象
设计原则
Service层应遵循"依赖倒置原则":
- 高层模块(Controller)不应依赖低层模块(Repository),两者都应依赖抽象
- 抽象不应依赖细节,细节应依赖抽象
- 使用接口定义服务契约,提供多种实现的可能性
学习目标
掌握如何设计和实现一个完整的Service类
Service接口定义
public interface OrderService {
OrderVO createOrder(OrderDTO orderDto) throws BusinessException;
void cancelOrder(Long orderId) throws OrderNotFoundException;
}
Service实现类
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrderServiceImpl implements OrderService {
private final OrderRepository orderRepository;
private final InventoryService inventoryService;
private final PaymentService paymentService;
public OrderServiceImpl(OrderRepository orderRepository,
InventoryService inventoryService,
PaymentService paymentService) {
this.orderRepository = orderRepository;
this.inventoryService = inventoryService;
this.paymentService = paymentService;
}
@Override
@Transactional(rollbackFor = BusinessException.class)
public OrderVO createOrder(OrderDTO orderDto) throws BusinessException {
if (!inventoryService.checkInventory(orderDto.getProductId(), orderDto.getQuantity())) {
throw new BusinessException("库存不足");
}
Order order = new Order();
inventoryService.deductInventory(orderDto.getProductId(), orderDto.getQuantity());
PaymentResult paymentResult = paymentService.processPayment(orderDto.getPaymentInfo());
if (!paymentResult.isSuccess()) {
throw new BusinessException("支付失败:" + paymentResult.getMessage());
}
Order savedOrder = orderRepository.save(order);
return convertToVO(savedOrder);
}
}
开发实践要点
- 始终基于接口编程,提高代码可测试性和可扩展性
- 使用构造器注入依赖,避免字段注入
- 每个方法应专注于单一业务目标
- 方法命名应清晰表达业务意图
- 避免在Service层直接操作数据库
- 保持Service方法无状态,避免存储业务状态
学习目标
掌握Spring事务管理机制,实现可靠的事务控制
1. 业务方法开始
2. 开启事务
3. 执行业务操作
4. 成功完成?
5. 提交事务
6. 出现异常?
7. 回滚事务
声明式事务管理
@Service
public class OrderServiceImpl implements OrderService {
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
timeout = 30,
rollbackFor = {BusinessException.class, SQLException.class})
public OrderVO createOrder(OrderDTO orderDto) {
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateInventory(Long productId, int quantity) {
}
}
传播行为
- REQUIRED:默认值,支持当前事务,不存在则新建
- SUPPORTS:支持当前事务,不存在则以非事务执行
- REQUIRES_NEW:新建事务,挂起当前事务
- NOT_SUPPORTED:非事务执行,挂起当前事务
- NEVER:非事务执行,存在事务则抛异常
- NESTED:嵌套事务执行
隔离级别
- DEFAULT:使用数据库默认隔离级别
- READ_UNCOMMITTED:读未提交(最低)
- READ_COMMITTED:读已提交(推荐)
- REPEATABLE_READ:可重复读
- SERIALIZABLE:串行化(最高)
常见事务错误
- 事务方法调用同一个类中的非事务方法
- 未正确配置rollbackFor导致异常不回滚
- 事务方法执行时间过长导致锁超时
- 嵌套事务配置不当导致数据不一致
- 在事务方法中处理不应该在事务中的操作(如发送邮件)
自定义业务异常
public class BusinessException extends RuntimeException {
private final String errorCode;
private final String errorMessage;
public BusinessException(String errorCode, String errorMessage) {
super(errorMessage);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
}
public class OrderNotFoundException extends BusinessException {
public OrderNotFoundException(Long orderId) {
super("ORDER_NOT_FOUND", "订单不存在,ID: " + orderId);
}
}
全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
ErrorResponse errorResponse = new ErrorResponse(
ex.getErrorCode(),
ex.getErrorMessage(),
LocalDateTime.now()
);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex) {
ErrorResponse errorResponse = new ErrorResponse(
"SYSTEM_ERROR",
"系统内部错误,请联系管理员",
LocalDateTime.now()
);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
异常处理最佳实践
- 为不同的业务错误定义特定的异常类
- 使用异常代码(errorCode)而非字符串匹配
- 在Service层抛出业务异常,在Controller层处理
- 避免在Service层捕获异常后直接记录日志
- 使用全局异常处理器统一处理异常
- 不要使用异常来控制业务逻辑流
学习目标
掌握使用JUnit和Mockito进行Service层单元测试
Service层单元测试示例
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private OrderRepository orderRepository;
@Mock
private InventoryService inventoryService;
@Mock
private PaymentService paymentService;
@InjectMocks
private OrderServiceImpl orderService;
@Test
void createOrder_Success() {
OrderDTO orderDto = new OrderDTO(1L, 2, "CREDIT_CARD");
Order expectedOrder = new Order(1L, 2, "PENDING");
Mockito.when(inventoryService.checkInventory(1L, 2)).thenReturn(true);
Mockito.when(paymentService.processPayment(any())).thenReturn(new PaymentResult(true, "Success"));
Mockito.when(orderRepository.save(any())).thenReturn(expectedOrder);
OrderVO result = orderService.createOrder(orderDto);
Assertions.assertNotNull(result);
Assertions.assertEquals(1L, result.getId());
Assertions.assertEquals("PENDING", result.getStatus());
Mockito.verify(inventoryService).deductInventory(1L, 2);
Mockito.verify(orderRepository).save(any());
}
@Test
void createOrder_InventoryCheckFailed() {
OrderDTO orderDto = new OrderDTO(1L, 10, "CREDIT_CARD");
Mockito.when(inventoryService.checkInventory(1L, 10)).thenReturn(false);
BusinessException exception = Assertions.assertThrows(BusinessException.class,
() -> orderService.createOrder(orderDto));
Assertions.assertEquals("库存不足", exception.getErrorMessage());
}
}
测试覆盖策略
- 覆盖所有主要业务路径
- 测试边界条件
- 测试异常场景
- 验证事务行为
- 测试并发情况
测试数据准备
- 使用Builder模式创建测试对象
- 使用随机数据生成器
- 准备边界值数据
- 使用内存数据库
- 避免依赖外部服务
测试执行策略
- 单元测试应快速执行
- 测试之间相互独立
- 避免测试业务逻辑实现细节
- 使用测试容器技术
- 集成测试覆盖端到端流程
1. 过度复杂的Service
单个Service类包含过多方法,承担太多职责
解决方法:根据业务领域拆分Service,遵循单一职责原则
2. 事务范围不当
事务范围过大导致性能问题,或过小导致数据不一致
解决方法:合理设置事务边界,只对需要事务的方法添加@Transactional
3. 异常处理不当
在Service层捕获异常后仅记录日志未抛出,导致事务未回滚
解决方法:在Service层抛出业务异常,由全局异常处理器处理
4. 循环依赖
ServiceA依赖ServiceB,同时ServiceB又依赖ServiceA
解决方法:重构代码提取公共逻辑,使用接口隔离,引入第三方类
5. 业务逻辑泄漏
业务逻辑写在Controller或Repository层
解决方法:严格分层,确保业务逻辑仅存在于Service层
6. 忽略并发问题
未考虑高并发场景下的数据一致性问题
解决方法:使用乐观锁、分布式锁、队列等机制处理并发
分层设计
- 严格遵循分层架构
- 使用接口定义服务契约
- 领域模型与数据模型分离
- 使用DTO在不同层间传输数据
事务管理
- 在Service层定义事务边界
- 设置合理的事务超时时间
- 避免在事务中执行远程调用
- 使用@Transactional(readOnly=true)优化查询
异常处理
- 使用特定业务异常
- 全局统一异常处理
- 提供有意义的错误信息
- 区分业务异常和系统异常
设计模式
- 策略模式:封装可替换的算法
- 模板方法:定义算法骨架
- 工厂模式:创建复杂对象
- 门面模式:简化复杂子系统调用
性能优化
- 避免N+1查询问题
- 批量处理数据库操作
- 使用缓存减少数据库访问
- 异步处理耗时操作
可测试性
- 依赖接口而非具体实现
- 使用依赖注入
- 避免静态方法和单例
- 分离业务逻辑和基础设施
开发检查清单
- ✅ 业务逻辑仅存在于Service层
- ✅ 每个Service类职责单一
- ✅ 使用接口定义服务契约
- ✅ 事务边界设置合理
- ✅ 异常处理机制完善
- ✅ 单元测试覆盖主要业务路径
- ✅ 性能关键操作经过优化
- ✅ 代码符合编码规范