Spring Boot @ExceptionHandler拦截异常的底层实现原理
异常拦截的底层执行逻辑
整个机制完全嵌入Spring MVC的核心请求处理链路,没有特殊魔法,执行步骤按顺序如下:
- 所有HTTP请求首先进入前端控制器
DispatcherServlet,请求路径匹配到你写的创建Person的POST接口后,由RequestMappingHandlerAdapter(注解Controller专属的处理器适配器)通过反射调用目标Controller方法。 - 目标方法执行过程中,只要出现未捕获的异常(不管是字段为null抛的校验异常、还是数据库操作抛的异常),会直接向上抛出,原方法的正常返回流程立刻终止,不会走后续的返回值序列化、视图渲染步骤。
DispatcherServlet捕获到向上抛的异常后,不会直接返回500错误给Servlet容器,而是遍历容器内所有注册的HandlerExceptionResolver实现类,按优先级依次尝试处理当前异常。@ExceptionHandler相关的逻辑,就由其中优先级较高的ExceptionHandlerExceptionResolver负责执行。ExceptionHandlerExceptionResolver启动阶段就会完成两类异常处理方法的缓存:一类是每个Controller内部定义的@ExceptionHandler方法,另一类是所有被@ControllerAdvice/@RestControllerAdvice标注的全局类里定义的@ExceptionHandler方法,同时会记录每个方法能匹配的异常类型映射。请求出现异常时,它会先匹配当前Controller的局部异常处理方法,匹配不到再查全局的缓存,找到能适配当前抛出异常类型的处理方法。- 找到匹配的方法后,Resolver通过反射调用这个异常处理方法,拿到返回值后包装成统一的
ModelAndView对象交回给DispatcherServlet,后续流程和正常Controller返回值的处理完全一致:比如返回ResponseEntity时,就会遍历配置好的HttpMessageConverter,把响应体序列化成客户端需要的JSON/XML格式,设置对应的HTTP状态码、响应头,最终写回给客户端。
异常处理器返回类型无需匹配原POST接口的核心原因
本质是异常触发后,原Controller方法的正常处理链路已经完全中断,异常处理器的返回值走的是独立的异常处理分支,和原方法的返回值没有任何绑定关系:
- 原Controller方法的返回值类型校验、适配逻辑,是
RequestMappingHandlerAdapter处理正常返回结果时才会执行的。一旦方法抛出异常,正常流程直接终止,根本不会走到原方法返回值的处理步骤,自然不会对异常处理方法的返回类型做和原方法一致的要求。 - 异常处理方法的返回值是由
HandlerExceptionResolver统一处理的,它只要求返回值能被后续的消息转换器、视图解析器正常解析即可:你可以返回ResponseEntity<ErrorResult>,也可以直接返回字符串错误提示、甚至返回错误页面的视图名,只要对应解析组件支持就行,和原接口正常返回Person对象还是其他类型完全没有耦合。
内容的提问来源于stack exchange,提问作者kairos
相关产品推荐
相关产品推荐

