先要明白
// 1. 用户发来请求: localhost:8080/user/1
// 2. 处理器映射器(HandlerMapping)的工作
// 它会找到对应的Controller和方法
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
return userService.getById(id);
}
// 3. 处理器适配器(HandlerAdapter)的工作
// - 把URL中的"1"转成Long类型的1 (类型转换)
// - 检查id是否为空、是否是正数 (数据验证)
// - 把转换好的参数传给controller方法 (参数解析)
我用生活中的例子解释 @RestControllerAdvice 的工作原理:
想象一个大型商场的客服中心:
- DispatcherServlet就像商场的总服务台
- 负责接收所有顾客的需求并分配给相应的专柜处理
- @RestControllerAdvice就像商场的客服中心
- 设立在商场的一个统一位置
- 专门处理各个专柜出现的问题和投诉
举个具体场景:
顾客在商场购物的流程:
1. 正常流程:
顾客 -> 总服务台(DispatcherServlet) -> 专柜(Controller) -> 购买商品
2. 出现问题时:
专柜(Controller)出现问题 -> 客服中心(@RestControllerAdvice)接手处理 -> 给顾客一个合理解释
用代码表示:
// 这就像设立一个统一的客服中心
@RestControllerAdvice
public class GlobalExceptionHandler {
// 这就像专门处理"商品缺货"的客服人员
@ExceptionHandler(ProductOutOfStockException.class)
public ResponseEntity<String> handleOutOfStock(ProductOutOfStockException e) {
// 返回友好提示:"非常抱歉,商品暂时缺货,预计3天后到货"
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body("商品暂时缺货,预计3天后到货");
}
// 这就像处理"商品价格异常"的客服人员
@ExceptionHandler(PriceException.class)
public ResponseEntity<String> handlePriceError(PriceException e) {
// 返回友好提示:"价格显示异常,请稍后重试"
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body("价格显示异常,请稍后重试");
}
}
为什么能拦截异常?
就像商场里:
- 所有专柜都在商场里面
- 客服中心在商场的入口处
- 一旦专柜出问题,就会按照商场规定,统一转到客服中心处理
在Spring中:
- DispatcherServlet(总服务台)知道有这个@RestControllerAdvice(客服中心)的存在
- 当Controller(专柜)抛出异常时
- DispatcherServlet就会把异常转给@RestControllerAdvice处理
关键点:
- 它不是拦截,而是异常发生后的统一处理
- Spring框架帮我们做了这个转发操作
- 这样我们就不用在每个Controller里写重复的异常处理代码