Spring捕获AOP抛出的异常
Spring捕获AOP抛出的异常
- 背景
- 解决过程
- 最初方案
- 失败探索
- 添加AOP
- 继承SDK的AOP类
- 修改AOP生效条件
- 最终解决方案——BeanPostProcessor
- 总结
背景
在最近开发中出现了这样的一个场景,有一个鉴权SDK引入到我的项目,他会对所有Controller进行鉴权,当然是通过自己定义Id-Token进行解析鉴权,如果Controller有权限则可以调用对应的Controller,如果不通过则直接抛出异常。
现在根据业务情况变更,要在以往的鉴权过程中新增一种情况,除了可以通过原有的Id-Token鉴权外,还可以通过另一个服务产生的Token字段进行鉴权,这两种情况只需要满足一个即可通过鉴权。
后面的解决方案记录了我尝试的各种过程,以及不采用失败原因,如果观众老爷不感兴趣,可以直接去看最终解决方案。
解决过程
最初方案
最直接能想到的方案肯定是重新修改鉴权的SDK,直接添加鉴权逻辑,然后重新编译。
但这个方案缺陷也很明显,这既然是个SDK,肯定是外部(其他部门)提供给我的,说不定啥时候就升级版本。修改它本身的鉴权过程,就是把两种鉴权过程耦合在一起了,那必然在后续升级过程中会产生很多麻烦,所以肯定不能这么干,这种方案PASS。
失败探索
添加AOP
“既然不能在他的SDK中做耦合,那我再写个AOP可以吗?”在放弃修改SDK的计划后,这个想法油然而生。
这个方案也是探究了很久,到最后确定不管是我们写个在它之前的AOP还是在它之后的AOP,都无法绕过原来鉴权的这个AOP。
我们把原有的AOP称为AOP1,新添加的AOP成为AOP2,如果AOP1放在AOP2之前,如果鉴权不通过,走不到AOP2就会报错返回;如果AOP2放在AOP1之前,那AOP2通过,AOP1还是会鉴权报错。所以这个方案也PASS了。
继承SDK的AOP类
秉持着“无法打败就加入”的想法,那既然我绕不开你,就加入你,或者说替代你。
所以我就想是不是可以自己重写一个AOP类,继承原有SDK的AOP类,把父类的鉴权逻辑全继承过来,捕获父类鉴权发生异常。
其实这个过程和上面的添加AOP本质上是一致的,因为你这个也是添加了一个AOP,而且继承并不会让原本的AOP失效,最后的结果就是回到了上面的情况。
修改AOP生效条件
原来AOP是通过注解生效的,它虽然把所有Controller都定义为切点,但只对加了@Auth注解生效,这个注解是SDK中自定的注解,那我可以重新定义一个注解去继承原来的Auth吗?
这个方案只要想一下就不可能,并不是说不能实现,而且是要自己增加很多和原有SDK重复的代码,还要改掉原来Controller上@Auth注解,这么巨大的改动出了问题谁负责,这个锅谁爱背谁背,反正我不背。因此这个办法也PASS了。
最终解决方案——BeanPostProcessor
回到问题最初,原本的AOP在鉴权不通过是抛出异常,之所以一直解决不了,主要问题是无法捕获这个异常,如果可以捕获异常,那我直接在报错的时候catch到然后重新处理不就行了吗。那如何无侵入的捕获这个异常呢?这个问题我查了很久都没找到答案,这也是我这篇文章起名的原因,希望可以帮助到有类似问题的小伙伴。
在我毫无头绪之时有个朋友说,如果能获取到代理对象,就能重新拓展这个方法,并且给我发了一段他在之前项目上做的处理。我看到了一丝丝曙光——BeanPostProcessor(后置处理器)。
BeanPostProcessor是什么我就不说详细阐述了(主要我也是一知半解),简单的说就是Spring帮你实例化Bean后,在执行初始化方法前后,可以执行一些自己的逻辑,然后返回bean。
那在这里返回的bean,你可以做一些手脚,在这里利用cglib重新对bean进行代理,进而完成对bean本身的功能的拓展加强。
当然以上只是我的理解,如果有问题可以一起讨论下,下面上代码。
@Component
@Slf4g
public class AuthAopBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (beanName.equals("authAop")){//cglib 动态代理 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(bean.getClass());// 可以自己去查一下这四个参数的含义 enhancer.setCallback((MethodInterceptor)(o,method,objects,methodProxy)->{// 仅对鉴权方法进行改造,其他方法正常执行返回if ( !"authMethod".equals(method.getName()){// 正常执行结果return methodProxy.invokeSuper(o, objects);}Object res = null;try{// 方法执行过程,在这里捕获异常,注意这里的第一个参数用bean,不能使用o,如果用o会造成发生无限递归造成StackOverflowres = method.invoke(bean,objects);} catch (Exception e){log.info(e.getCause().getMessage());if (checkToken()){res = methodProxy.invokeSuper(o, objects);} else {throw e;} } return res; });// 返回新的代理beanreturn enhancer.create();} return bean;} // 该逻辑自己修改补充private boolean checkToken() {return true; }
}
关键点我都写在注释里了,因为公司的代码是保密的,所以我只是把思路搬了过来,大家想用的时候可以自己改一下。这样就利用BeanPostProcessor,无侵入的对之前的AOP过程进行了拓展。
总结
这次探索还是很有收获的,对Spring的BeanPostProcessor有了实际的使用经验,也稍微学习了一下cglib动态代理,但对这方面还没有深入研究,所以也不多发言。
当然这只是我自己研究的结果,并不一定是最好的方法,如果有哪位大佬有更好的方法,麻烦告知一下,我去学习一下(我真的是因为没在网上找到更好的方法才自己研究的)。
Spring作为Java开发的业界标准,要学习的东西还很多,总之继续加油吧~~~
Spring捕获AOP抛出的异常相关推荐
- java 异常 不抛,java中不捕获或抛出的异常
java中不捕获或抛出的异常 发布时间:2020-06-25 14:29:16 来源:亿速云 阅读:137 作者:Leah 这期内容当中小编将会给大家带来有关java中不捕获或抛出的异常,文章内容丰富 ...
- java捕获定时器抛出的异常_详细了解Java中定时器Timer的使用及缺陷分析
在需要定时并且周期执行任务时,在最初的JAVA工具类库中,Timer可以实现任务的定时周期执行的需求,不过有一定的缺陷,比如,Timer是基于绝对时间而非相对时间,因此Timer对系统时钟比较敏感,本 ...
- ORACLE 存储过程异常捕获并抛出
ORACLE 存储过程异常捕获并抛出 参考文章: (1)ORACLE 存储过程异常捕获并抛出 (2)https://www.cnblogs.com/wdw31210/p/7009731.html 备忘 ...
- Java中主线程如何捕获子线程抛出的异常
Java中主线程如何捕获子线程抛出的异常 参考文章: (1)Java中主线程如何捕获子线程抛出的异常 (2)https://www.cnblogs.com/jpfss/p/10272885.html ...
- java 异常捕获抛出_JAVA异常处理捕获与抛出原理解析
JAVA 异常 当代码运行出现错误导致程序终止运行或出现错误情况的状况,就是异常.异常不是指语法错误,即不属于编译错误,只有运行的程序才会有异常. 这个时候,JAVA 就提供了优秀的处理方法:异常处理 ...
- .NET 指南:捕获并且抛出标准的异常类型
下列指导方针为 .NET Framework 所提供的一些最常用的异常而描述了最佳的实践.关于 .NET Framework 所提供的完整的异常类列表,请参考:[.NET Framework 类库参考 ...
- Spring事务抛出Exception异常不回滚
今天有个业务逻辑流程为: 1.访客预约确认先更新预约状态为"预约确认" 2.调用http接口发短信.更新预约状态为"预约成功"等一系列操作 这里面有个问题,如果 ...
- 【java基础】java异常,捕获与抛出
java异常,捕获与抛出 1.概念: 在java里,所有的异常都有一个共同的祖先Throwable(可抛出). Throwable:有两个重要的子类:Exception(异常)和Error(错误). ...
- oracle异常抛出,ORACLE 存储过程异常捕获并抛出
for tab_name in tables loop execute immediate 'drop table '||tab_name; --此处可能会报错 end loop; 当前情况是,循环表 ...
最新文章
- 《OpenCV3编程入门》学习笔记1 邂逅OpenCV
- 53年来国内唯三,MindSpore加速昇腾芯片论文获国际顶会MICRO最佳论文提名
- 机器学习十大算法之EM算法
- pythonexcelweb交互插件_来一次Python与Excel的完美交互
- 在win7环境下使用网络无线共享把电脑变成一台无线路由器
- linux进程如何挂起自己,Linux Server HTTP进程每天挂起服务器
- JavaScript 统计中英混合字符串的长度
- 光头强的圆球机器人视频_《熊出没狂野大陆》快上映了,看了多年光头强,还能有新鲜动画吗...
- hpm1216nfh驱动程序_惠普m1216nfh打印机驱动
- 几台服务器做虚拟化,4台服务器虚拟化教程(多台服务器虚拟成一台)
- 通达OA应对后门检测的临时方法
- 浅析百度有啊生活平台未来发展的八大潜力特征
- myeclipse删除jar时出错,无法删除怎么办
- gartner数据治理_Gartner:2019年「数据管理解决方案」魔力象限
- Leetcode刷题面试题 16.14. 最佳直线
- 什么是防火墙?服务器防火墙建议开启吗?
- hive partition简介
- 人工智能领域期刊总结
- 基于Matlab计算天线阵列方向图和绘制方向图
- SQL SERVER 2016 AlwaysOn搭建实例
热门文章
- js typeof undefined
- NOI openjudge 计算2的N次方
- 数据库拆分3--使用sharding-jdbc 支持子查询sql
- ERP的实施--把握三大计划
- LeetCode 974. 和可被 K 整除的子数组 | Python
- 前端性能优化之“离线缓存manifest”
- 计算机一级考试试题excel,计算机一级考试模拟题(word、excel、ppt以及基础知识);...
- 58同城 Java 笔(面)试题(整理自群友)
- 前端开发:JS中join()方法的使用总结
- 苹果cms用拼音伪静态之后,播放页密码访问失效解决方法