如何优雅处理Spring Boot服务层异常?
优雅处理Spring Boot服务层异常的几种方案
方案一:自定义业务异常 + 全局异常处理器
这是最贴合Spring分层思想的方案:服务层只负责抛出语义明确的自定义业务异常,异常统一由上层的@ControllerAdvice全局处理器捕获处理,既不破坏分层逻辑,又能统一异常响应格式。
- 定义自定义业务异常
public class BusinessException extends RuntimeException { private Integer code; private String message; public BusinessException(Integer code, String message) { super(message); this.code = code; this.message = message; } // getter方法省略 }
- 服务层抛出业务异常
@Service public class UserService { public void updateUser(Long userId) { if (userId == null || userId <= 0) { throw new BusinessException(400, "用户ID不合法"); } // 执行业务逻辑,若遇底层异常(如数据库异常),也可转换为业务异常抛出 try { // 数据库操作 } catch (DataAccessException e) { throw new BusinessException(500, "用户信息更新失败"); } } }
- 全局处理器捕获处理
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) @ResponseBody public ResponseEntity<Map<String, Object>> handleBusinessException(BusinessException e) { Map<String, Object> result = new HashMap<>(); result.put("code", e.getCode()); result.put("message", e.getMessage()); return new ResponseEntity<>(result, HttpStatus.valueOf(e.getCode())); } // 可额外处理其他系统异常,如RuntimeException、IOException等 }
方案二:AOP + 自定义注解实现精准异常处理
如果部分服务方法需要局部异常处理(比如返回默认值、做补偿操作,而非抛到控制器层),可以用AOP结合自定义注解,只对标记的方法做异常处理,避免@Around捕获所有方法造成资源浪费。
- 定义自定义注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface HandleServiceException { // 可添加属性,比如指定默认返回值类型 }
- 编写AOP切面
@Aspect @Component public class ServiceExceptionAspect { private static final Logger logger = LoggerFactory.getLogger(ServiceExceptionAspect.class); @Pointcut("@annotation(com.example.demo.HandleServiceException)") public void serviceExceptionPointcut() {} @Around("serviceExceptionPointcut()") public Object handleException(ProceedingJoinPoint joinPoint) { try { return joinPoint.proceed(); } catch (Throwable throwable) { // 记录异常日志 logger.error("服务方法执行异常,方法名:{},参数:{}", joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs()), throwable); // 根据异常类型做不同处理,比如返回默认值 if (throwable instanceof BusinessException) { return new ErrorResponse(((BusinessException) throwable).getCode(), throwable.getMessage()); } // 未知异常返回默认兜底响应 return new ErrorResponse(500, "服务内部处理异常"); } } }
- 服务层方法标记注解
@Service public class OrderService { @HandleServiceException public Order getOrder(Long orderId) { if (orderId <= 0) { throw new BusinessException(400, "订单ID不合法"); } return orderRepository.findById(orderId) .orElseThrow(() -> new RuntimeException("订单不存在")); } }
方案三:服务层局部异常转换
对于不需要抛到上层的底层异常(如数据库操作异常、第三方调用异常),可以在服务层局部捕获并转换为业务友好的提示,或者做降级处理,避免上层感知到底层实现细节。
@Service public class ProductService { public List<Product> getHotProducts() { try { return productRepository.findHotProducts(); } catch (DataAccessException e) { logger.error("热门商品查询失败", e); // 降级处理,返回空列表或默认数据 return Collections.emptyList(); } } }
内容的提问来源于stack exchange,提问作者buzz
相关产品推荐
相关产品推荐

