介绍
本篇文章给大家分享的是有关使用弹簧编写一个全局异常拦截器,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
<强>为什么要重复造轮子强>
你可能会问,春天已经自带了全局异常拦截,为什么还要重复造轮子呢?
这是个好问题,我觉得有以下几个原因
- <李>装逼李> <李>春季的全局异常拦截只是针对于spring MVC的接口,对于你的RPC接口就无能为力了李> <>李无法定制化李> <李>除了写业务代码,我们其实还能干点别的事
李,>
我觉得上述理由已经比较充分的解答了为什么要重复造轮子,接下来就来看一下怎么造轮子
<强>造个什么样的轮子? 强>
我觉得全局异常拦截应该有如下特性
- <李>使用方便,最好和春天原生的使用方式一致,降低学习成本李> <李>能够支持所有接口李> <李>调用异常处理器可预期,比如说定义了RuntimeException的处理器和异常的处理器,如果这个时候抛出NullPointException,这时候要能没有歧义的选择预期的处理器
李,>
<>强如何造轮子? 强>
由于现在的应用基本上都是基于春天的,因此我也是基于SpringAop来实现全局异常拦截
首先先定义几个注解
@Target (ElementType.TYPE) @Retention (RetentionPolicy.RUNTIME) @Documented @ component 公共@ interface ExceptionAdvice { } @Target (ElementType.METHOD) @Retention (RetentionPolicy.RUNTIME) @Documented 公共@ interface ExceptionHandler { Class<及# 63;扩展Throwable>[]值(); } @Target (ElementType.METHOD) @Retention (RetentionPolicy.RUNTIME) @Documented 公共@ interface ExceptionIntercept { }
@ExceptionAdvice的作用是标志定义异常处理器的类,方便找到异常处理器
@ExceptionHandler的作用是标记某个方法是处理异常的,里面的值是能够处理的异常类型
@ExceptionIntercept的作用是标记需要异常拦截的方法
接下来定义统一返回格式,以便出现错误的时候统一返回
@ data 公开课BaseResponse{ 私人整数代码; 私人字符串消息; 私人T数据; 公共BaseResponse(整数代码字符串消息){ 这一点。代码=代码; 这一点。消息=消息; } }
然后定义一个收集异常处理器的类
公共类ExceptionMethodPool { 私人List方法; 私有对象excutor; 公共ExceptionMethodPool(对象excutor) { 这一点。方法=new ArrayList (); 这一点。excutor=excutor; } 公共对象getExcutor () { 返回excutor; } 公共空间添加(Class<及# 63;Throwable>延伸;clazz方法方法){ 方法。添加(新ExceptionMethod (clazz、方法)); }//按序查找能够处理该异常的处理器 公共方法obtainMethod (Throwable Throwable) { 返回的方法 .stream () .filter (e→e.getClazz () .isAssignableFrom (throwable.getClass ())) .findFirst () .orElseThrow(()→新RuntimeException(“没有找到对应的异常处理器“)) .getMethod (); } @AllArgsConstructor @ getter 类ExceptionMethod { 私人Class<及# 63;Throwable>延伸;clazz; 私有方法方法; } }
ExceptionMethod里面有两个属性
- <李> clazz:这个代表着能够处理的异常李> <李>方法:代表着处理异常调用的方法
李,>
ExceptionMethodPool里面按序存放所有异常处理器,excutor是执行这些异常处理器的对象
接下来把所有定义的异常处理器收集起来
@ component 公共类ExceptionBeanPostProcessor实现BeanPostProcessor { 私人ExceptionMethodPool ExceptionMethodPool; @ autowired 私人ConfigurableApplicationContext上下文; @Override 公共对象postProcessBeforeInitialization(对象bean,字符串beanName)抛出BeansException { Class<及# 63;比;clazz=bean.getClass (); ExceptionAdvice建议=clazz.getAnnotation (ExceptionAdvice.class); 如果返回bean(建议==null); 如果(exceptionMethodPool !=null)把新RuntimeException(“不允许有两个异常定义类“); exceptionMethodPool=new exceptionMethodPool (bean);//保持处理异常方法顺序 Arrays.stream (clazz.getDeclaredMethods ()) .filter(方法→method.getAnnotation (ExceptionHandler.class) !=null) .forEach(方法→{ ExceptionHandler ExceptionHandler=method.getAnnotation (ExceptionHandler.class); Arrays.stream (exceptionHandler.value ())。forEach (c→exceptionMethodPool.add (c,方法)); });//注册进春容器 context.getBeanFactory () .registerSingleton (“exceptionMethodPool" exceptionMethodPool); 返回bean; } }春天使用编写一个全局异常拦截器