医程通 网站做的太,学科建设的网站,网站建设 软件有哪些内容,小游戏代理平台文章目录 springmvc 全局异常处理器配置的三种方式深入底层源码分析原理配置全局异常处理器的三种方式实现接口HandlerExceptionResolver并配置到WebMvcConfigurer注解式配置ExceptionHandlercontroller里方法上定义ExceptionHandler 深入源码分析进入DispatcherServlet执… 文章目录 springmvc 全局异常处理器配置的三种方式深入底层源码分析原理配置全局异常处理器的三种方式实现接口HandlerExceptionResolver并配置到WebMvcConfigurer注解式配置ExceptionHandlercontroller里方法上定义ExceptionHandler 深入源码分析进入DispatcherServlet执行handler方法并catch异常 springmvc 全局异常处理器配置的三种方式深入底层源码分析原理
配置全局异常处理器的三种方式
实现接口HandlerExceptionResolver并配置到WebMvcConfigurer
定义一个类实现这个接口 /*** Interface to be implemented by objects that can resolve exceptions thrown during* handler mapping or execution, in the typical case to error views. Implementors are* typically registered as beans in the application context.** pError views are analogous to JSP error pages but can be used with any kind of* exception including any checked exception, with potentially fine-grained mappings for* specific handlers.** author Juergen Hoeller* since 22.11.2003*/
public interface HandlerExceptionResolver {/*** Try to resolve the given exception that got thrown during handler execution,* returning a {link ModelAndView} that represents a specific error page if appropriate.* pThe returned {code ModelAndView} may be {linkplain ModelAndView#isEmpty() empty}* to indicate that the exception has been resolved successfully but that no view* should be rendered, for instance by setting a status code.* param request current HTTP request* param response current HTTP response* param handler the executed handler, or {code null} if none chosen at the* time of the exception (for example, if multipart resolution failed)* param ex the exception that got thrown during handler execution* return a corresponding {code ModelAndView} to forward to,* or {code null} for default processing in the resolution chain*/NullableModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Nullable Object handler, Exception ex);}定义一个配置类实现接口 WebMvcConfigurer 并重写方法configureHandlerExceptionResolvers将你定义的这个resolver加入到resolvers这个List即可
注解式配置ExceptionHandler
要让spring扫描到这个类一种方式是直接把这个类放到springboot能扫描的目录下启动类当前目录或者子目录或者在启动类手动配置的扫描组件目录另一种方式是放在工具类里定义到spring.factories里让spring扫描到注意设置个Order(0)让这个优先于springmvc注解错误处理器之前注入到容器否则会无效因为那个处理器只加载一次后续不会再扫容器里的实例了。
/*** author humorchen* date: 2024/6/11* description: 认证全局异常配置**/Slf4j
RestControllerAdvice
Order(0)
public class AuthExceptionResolver {public static final int TOKEN_ERROR_CODE HttpStatus.HTTP_UNAUTHORIZED;public static final String ERROR_MSG 未登录或登录已失效!;/*** TOKEN相关异常** param e* return*/ExceptionHandler({SaTokenException.class, JWTVerificationException.class})public ResultString resolveAuthTokenException(Exception e) {log.info(handleSaTokenException {}: {}, e.getClass().getName(), e.getMessage());return Result.fail(TOKEN_ERROR_CODE, ERROR_MSG);}}
看到上面代码你会有一个疑问要是定义了多个它怎么确定该用哪个呢源码中使用ExceptionDepthComparator排序排序规则是按照继承顺序来(继承关系越靠近数值越小当前类最小为0,顶级父类Throwable为int最大值)排序之后选取继承关系最靠近的那个并且ExceptionHandlerMethodResolver的exceptionLookupCache中key为当前抛出的异常value为解析出来的匹配method. org.springframework.core.ExceptionDepthComparator
controller里方法上定义ExceptionHandler
RestController
public class ExceptionController {ExceptionHandler(Exception.class)public ResponseEntityString handleException(Exception ex) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(An error occurred: ex.getMessage());}RequestMapping(/test)public String test() throws Exception {throw new Exception(Test exception!);}
}
深入源码分析
进入DispatcherServlet 执行handler方法并catch异常 调用processDispatchResult处理分发处理结果
发现有异常则调用processHandlerException 处理handler异常
如果有handlerExceptionResovlers就循环调用直到有一个resolver返回了非空值就跳出循环 默认resolver就是把这个异常放到了request.attribute里头去了 HandlerExceptionResolverComposite,是handler异常处理器的复合类把这些resolver放到它内部一个集合 点开这个类源码看下其实就是在我们在webmvcconfigurer里配置这个方法的时候把配置的所有resolver设置进这个类里的this.resolvers 集合去了进调试可以看到有两个resolver一个是我自定义并配置上的ExceptionResolver另一个是个Http请求异常处理器seata的HttpHandlerExceptionResolver 分布式事务用来清理ID的不用管 第三个也是我注入的那个错误处理器 如果经过这些处理器有一个返回了非空的结果这个异常 处理就会被结束 执行完所有的resolver有一个返回了ModelAndView的话就返回这个ModelAndView否则继续抛出异常往上层传递。然后触发请求handler的triggerAfterCompletion也就是执行你所有跟这个handler绑定的HandlerInterceptor的afterCompletion 前面看的都是HandlerExceptionResolver的实现还有那个注解式的实现我们也一起看下这个ExceptionHandlerExceptionResolver 类的源码就是用来处理 ExceptionHandler的
/*** An {link AbstractHandlerMethodExceptionResolver} that resolves exceptions* through {code ExceptionHandler} methods.** pSupport for custom argument and return value types can be added via* {link #setCustomArgumentResolvers} and {link #setCustomReturnValueHandlers}.* Or alternatively to re-configure all argument and return value types use* {link #setArgumentResolvers} and {link #setReturnValueHandlers(List)}.** author Rossen Stoyanchev* author Juergen Hoeller* since 3.1*/
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolverimplements ApplicationContextAware, InitializingBean {Nullableprivate ListHandlerMethodArgumentResolver customArgumentResolvers;Nullableprivate HandlerMethodArgumentResolverComposite argumentResolvers;Nullableprivate ListHandlerMethodReturnValueHandler customReturnValueHandlers;Nullableprivate HandlerMethodReturnValueHandlerComposite returnValueHandlers;private ListHttpMessageConverter? messageConverters;private ContentNegotiationManager contentNegotiationManager new ContentNegotiationManager();private final ListObject responseBodyAdvice new ArrayList();Nullableprivate ApplicationContext applicationContext;private final MapClass?, ExceptionHandlerMethodResolver exceptionHandlerCache new ConcurrentHashMap(64);private final MapControllerAdviceBean, ExceptionHandlerMethodResolver exceptionHandlerAdviceCache new LinkedHashMap();
在这个bean执行到afterPropertiesSet这个时间点的时候进行了 ExceptionHandler实现的扫描initExceptionHandlerAdviceCache()如果你的 ExceptionHandler配置bean在这个时间点之后注入进IOC容器的话那么恭喜你你的配置不会生效因此前面我说要让自己的这个配置优先级变高才能生效。 ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()) 把所有的标注了ControllerAdvice注解的bean全部找出来并根据order排序返回了 看到这你会疑问还有个RestControllerAdvice来看源码,RestControllerAdvice上标注了ControllerAdvice 前面已经看了加载过程现在看执行处理异常的代码其实就是拿着加载好的处理器去调用处理然后参数呢又是可选的随便你定义那个其实是反射拿到你方法定义的参数然后跟现有能给你的一些参数比对类型然后组成一个参数数组传入执行对象、参数反射执行方法然后就拿到了异常处理结果 组装生成调用参数数组的方法就在这org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues 也就是把异常对象和当前的handlerMethod给你了你自己可以写到参数里它会注入给你的 ExceptionHandler原理另一个作者也写的不错 从源码角度去深入分析关于Spring的异常处理ExceptionHandler的实现原理