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抛出的异常相关推荐

  1. java 异常 不抛,java中不捕获或抛出的异常

    java中不捕获或抛出的异常 发布时间:2020-06-25 14:29:16 来源:亿速云 阅读:137 作者:Leah 这期内容当中小编将会给大家带来有关java中不捕获或抛出的异常,文章内容丰富 ...

  2. java捕获定时器抛出的异常_详细了解Java中定时器Timer的使用及缺陷分析

    在需要定时并且周期执行任务时,在最初的JAVA工具类库中,Timer可以实现任务的定时周期执行的需求,不过有一定的缺陷,比如,Timer是基于绝对时间而非相对时间,因此Timer对系统时钟比较敏感,本 ...

  3. ORACLE 存储过程异常捕获并抛出

    ORACLE 存储过程异常捕获并抛出 参考文章: (1)ORACLE 存储过程异常捕获并抛出 (2)https://www.cnblogs.com/wdw31210/p/7009731.html 备忘 ...

  4. Java中主线程如何捕获子线程抛出的异常

    Java中主线程如何捕获子线程抛出的异常 参考文章: (1)Java中主线程如何捕获子线程抛出的异常 (2)https://www.cnblogs.com/jpfss/p/10272885.html ...

  5. java 异常捕获抛出_JAVA异常处理捕获与抛出原理解析

    JAVA 异常 当代码运行出现错误导致程序终止运行或出现错误情况的状况,就是异常.异常不是指语法错误,即不属于编译错误,只有运行的程序才会有异常. 这个时候,JAVA 就提供了优秀的处理方法:异常处理 ...

  6. .NET 指南:捕获并且抛出标准的异常类型

    下列指导方针为 .NET Framework 所提供的一些最常用的异常而描述了最佳的实践.关于 .NET Framework 所提供的完整的异常类列表,请参考:[.NET Framework 类库参考 ...

  7. Spring事务抛出Exception异常不回滚

    今天有个业务逻辑流程为: 1.访客预约确认先更新预约状态为"预约确认" 2.调用http接口发短信.更新预约状态为"预约成功"等一系列操作 这里面有个问题,如果 ...

  8. 【java基础】java异常,捕获与抛出

    java异常,捕获与抛出 1.概念: 在java里,所有的异常都有一个共同的祖先Throwable(可抛出). Throwable:有两个重要的子类:Exception(异常)和Error(错误). ...

  9. oracle异常抛出,ORACLE 存储过程异常捕获并抛出

    for tab_name in tables loop execute immediate 'drop table '||tab_name; --此处可能会报错 end loop; 当前情况是,循环表 ...

最新文章

  1. 《OpenCV3编程入门》学习笔记1 邂逅OpenCV
  2. 53年来国内唯三,MindSpore加速昇腾芯片论文获国际顶会MICRO最佳论文提名
  3. 机器学习十大算法之EM算法
  4. pythonexcelweb交互插件_来一次Python与Excel的完美交互
  5. 在win7环境下使用网络无线共享把电脑变成一台无线路由器
  6. linux进程如何挂起自己,Linux Server HTTP进程每天挂起服务器
  7. JavaScript 统计中英混合字符串的长度
  8. 光头强的圆球机器人视频_《熊出没狂野大陆》快上映了,看了多年光头强,还能有新鲜动画吗...
  9. hpm1216nfh驱动程序_惠普m1216nfh打印机驱动
  10. 几台服务器做虚拟化,4台服务器虚拟化教程(多台服务器虚拟成一台)
  11. 通达OA应对后门检测的临时方法
  12. 浅析百度有啊生活平台未来发展的八大潜力特征
  13. myeclipse删除jar时出错,无法删除怎么办
  14. gartner数据治理_Gartner:2019年「数据管理解决方案」魔力象限
  15. Leetcode刷题面试题 16.14. 最佳直线
  16. 什么是防火墙?服务器防火墙建议开启吗?
  17. hive partition简介
  18. 人工智能领域期刊总结
  19. 基于Matlab计算天线阵列方向图和绘制方向图
  20. SQL SERVER 2016 AlwaysOn搭建实例

热门文章

  1. js typeof undefined
  2. NOI openjudge 计算2的N次方
  3. 数据库拆分3--使用sharding-jdbc 支持子查询sql
  4. ERP的实施--把握三大计划
  5. LeetCode 974. 和可被 K 整除的子数组 | Python
  6. 前端性能优化之“离线缓存manifest”
  7. 计算机一级考试试题excel,计算机一级考试模拟题(word、excel、ppt以及基础知识);...
  8. 58同城 Java 笔(面)试题(整理自群友)
  9. 前端开发:JS中join()方法的使用总结
  10. 苹果cms用拼音伪静态之后,播放页密码访问失效解决方法