一个关于IntroductionAdvisor的bug

一个关于IntroductionAdvisor的bug


public class TestMain {public static void main(String[] args) {// 1. 准备被代理的目标对象People peo = new People();// 2. 准备代理工厂ProxyFactory pf = new ProxyFactory();// 3. 准备introduction advice,advice 持有需要额外添加的接口Developer和Developer接口的实现类DelegatingIntroductionInterceptor dii = new DelegatingIntroductionInterceptor((Developer) () -> System.out.println("编码"));// 4. 添加advice和代理对象需要继承的接口pf.addAdvice(dii);// 5. 设置被代理对象pf.setTarget(peo);// 6. 这里强制类型转换会失败,因为代理对象采用JDK进行动态代理,只实现了Developer接口和Spring AOP内部接口//  这里按理应该采用Cglib代理才对 !!!peo = (People) pf.getProxy();peo.drink();peo.eat();// 7. 强制转换为Developer接口,实际方法调用会被introduction advice拦截,调用请求转发给了advice内部持有的Developer接口实现类Developer developer = (Developer) peo;developer.code();}public static class People {void eat() {System.out.println("eat");}void drink() {System.out.println("drink");}}public interface Developer {void code();}
}

运行结果:

Exception in thread "main" java.lang.ClassCastException: class com.sun.proxy.$Proxy0 cannot be cast to class com.spring.TestMain$People (com.sun.proxy.$Proxy0 and com.spring.TestMain$People are in unnamed module of loader 'app')at com.spring.TestMain.main(TestMain.java:20)

这里原本是期望代理对象能够采用Cglib进行代理的,因为目标对象没有实现任何接口,但是却因为ProxyFactory特殊处理了类型为IntroductionAdvisor的切面,将IntroductionAdvisor提供的接口都加入到了AdvisedSupport的interfaces接口集合中;导致DefaultAopProxyFactory最终执行代理时,选择采用jdk而非cglib。

所以我们得到的代理对象实际采用jdk实现动态代理,实现了Spring AOP模块内部相关接口和Developer接口,当我们强制将代理对象转换为People类型时,会抛出类型转换异常。


Spring AOP 模块版本为: 5.3.9

在这里插入图片描述

原因:

AdvisedSupport 在添加advice的时候会特殊处理IntroductionInfo类型的Advice , 将其额外实现的接口添加到interfaces接口集合中去 :

	@Overridepublic void addAdvice(Advice advice) throws AopConfigException {int pos = this.advisors.size();addAdvice(pos, advice);}@Overridepublic void addAdvice(int pos, Advice advice) throws AopConfigException {Assert.notNull(advice, "Advice must not be null");if (advice instanceof IntroductionInfo) {// We don't need an IntroductionAdvisor for this kind of introduction:// It's fully self-describing.addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));}...}@Overridepublic void addAdvisor(int pos, Advisor advisor) throws AopConfigException {if (advisor instanceof IntroductionAdvisor) {validateIntroductionAdvisor((IntroductionAdvisor) advisor);}addAdvisorInternal(pos, advisor);}private void validateIntroductionAdvisor(IntroductionAdvisor advisor) {advisor.validateInterfaces();// If the advisor passed validation, we can make the change.Class<?>[] ifcs = advisor.getInterfaces();for (Class<?> ifc : ifcs) {addInterface(ifc);}}

​ 此时即便目标对象没有实现接口,interfaces集合也不会为空:

	private List<Class<?>> interfaces = new ArrayList<>();

这会导致DefaultAopProxyFactory选择是采用jdk还是cglib进行动态代理时,错误的选择JDK而非cglib进行动态代理,因此最终得到的代理对象不能够强制转换为目标对象类型,这与我们预期目标不符合:

	@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (!NativeDetector.inNativeImage() &&(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}}// interfaces集合此时不为空,所以会采用jdk进行动态代理private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {Class<?>[] ifcs = config.getProxiedInterfaces();return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));}

我不确定这边是否算是一个bug , 如果可以的话, 我更期望这边能够单独处理一下IntroductionAdvisor额外提供的接口列表,避免在目标对象没有实现接口的前提下,还是选择采用JDK动态代理。


笔者目前不太确定这是否算做一个bug,目前已将该问题反馈给Spring官方团队,Issue链接如下:

  • A bug related to IntroductionAdvisor

关于IntroductionAdvisor的用法,可以参考我之前写的这篇文章进行学习:

  • Seata 源码篇之AT模式启动流程 - 上 - 02

2023-09-26 Spring官方回复

在这里插入图片描述
简而言之就是确实存在这个bug,但是目前只能临时性强制采用cglib动态代理解决,后期会改进。

各位小伙伴使用IntroductionAdvisor的时候可以注意一下,不要踩了这个坑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/89307.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

影刀自动化采集底层逻辑

hello,大家好&#xff0c;这里是【玩数据的诡途】 接上回 <我的影刀故事> 今天给大家介绍一下整个采集的底层逻辑&#xff0c;包括业务流程自动化也是基于这一套基础逻辑进行展开的&#xff0c;顺便带大家熟悉一下影刀&#xff0c;既然叫影刀系列了&#xff0c;那后续一些…

13基于PCA的人脸识别,程序已调通,可将自己的数据替换进行识别,得到识别准确率结果,MATLAB平台。

基于PCA的人脸识别&#xff0c;程序已调通&#xff0c;可将自己的数据替换进行识别&#xff0c;得到识别准确率结果&#xff0c;MATLAB平台。

xorm数据库操作之Join、Union

golang的数据库操作xorm使用起来非常方便&#xff0c;不用再自己写SQl语句&#xff0c;而且xorm自己给我们做了SQL防注入等操作&#xff0c;用起来既方便又安全。此次文章我不会记录xorm的基本操作&#xff0c;我值记录一些特殊用法问题&#xff0c;包括动态创建表单、基于xorm…

CSS详细基础(二)文本样式

插播一条CSS的工作原理&#xff1a; CSS是一种定义样式结构如字体、颜色、位置等的语言&#xff0c;被用于描述网页上的信息格式化和显示的方式。CSS样式可以直接存储于HTML网页或者单独的样式单文件。无论哪一种方式&#xff0c;样式单包含将样式应用到指定类型的元素的规则。…

oracle 递归

1&#xff09;此方法&#xff0c;父亲state 9999&#xff0c;儿子state ! 9999&#xff0c;儿子能查询出来 select * from T_ORGANIZATION ot where ot.state!9999 start with ot.id 7 connect by prior ot.id ot.ORG_PARENTID order by ot.id asc 2&#xff09;此方法…

【node】发送邮件及附件简要使用说明

Nodemailer是一个用于Node.js应用程序的模块&#xff0c;可以轻松发送电子邮件。该项目始于2010年&#xff0c;当时没有合理的选项来发送电子邮件消息&#xff0c;如今它是大多数Node.js用户默认选择的解决方案。 一、环境配置 安装模块&#xff1a;nodemailer npm i nodema…

从代码操作层面解释什么是“面相对象编程”?

起因&#xff1a; 今天开了一个小会&#xff0c;会上朋友给我们说了一个事&#xff0c;Java项目上他开发一个小功能 用了很多代码&#xff0c;项目经理发现代码太多&#xff0c;说要优化一下&#xff0c;然后亲自帮同事优化&#xff0c;结果是查库的代码少了至少10条sql&#x…

Java如何解决浮点数计算不精确问题

有的时候博客内容会有变动&#xff0c;首发博客是最新的&#xff0c;其他博客地址可能会未同步,认准https://blog.zysicyj.top 首发博客地址[1] 面试题手册[2] 系列文章地址[3] 1. 什么是浮点数计算不精确问题? 在 Java 中&#xff0c;浮点数计算不精确问题指的是使用浮点数进…

接口测试——接口协议抓包分析与mock_L2

目录&#xff1a; 抓包工具charles抓包工具fiddler抓包工具证书配置app抓包实战练习接口测试实战练习 1.抓包工具charles 工具介绍 支持 SSL 代理支持流量控制支持重发网络请求&#xff0c;方便后端调试支持修改网络请求参数支持网络请求的截获并动态修改可以自动将 json 或…

电子电子架构——AUTOSAR信息安全机制有哪些?(上)

电子电子架构——AUTOSAR信息安全机制有哪些&#xff08;上&#xff09; 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 人们会在生活中不断攻击你。他们的主要…

Mysql 子查询,最值查询

1.leetcode-184&#xff1a;查找部门内最高的薪水 首先是一个子查询&#xff0c;找出一个部门里最高的那个工资 随后查找最终需要的值&#xff0c;并且部门编号以及薪水应该包含在这个子查询中 最终答案&#xff1a; # Write your MySQL query statement below SELECT Depar…

爬虫获取一个网站内所有子页面的内容

上一篇介绍了如何爬取一个页面内的所有指定内容&#xff0c;本篇讲的是爬去这个网站下所有子页面的所有指定的内容。 可能有人会说需要的内容复制粘贴&#xff0c;或者直接f12获取需要的文件下载地址一个一个下载就行了&#xff0c;但是如下图十几个一级几十个二级一百多个疾病…

网工内推 | 网络工程师,软考证书优先,六险一金,包吃

01 科力信息 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、负责蚌埠项目的设备安装及调试&#xff1b; 2、对边界网络运行中的监控、故障排除、问题处理。 任职要求&#xff1a; 1、2年及以上网络相关工作经验&#xff0c;有交通管理网络运维经验优先&#xff1b…

webview_flutter

查看webview内核 ​https://liulanmi.com/labs/core.html​ h5中获取设备 https://cloud.tencent.com/developer/ask/sof/105938013 https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator/mediaDevices web资源部署后navigator获取不到mediaDevices实例的解决方案&…

重新认识Java中的死锁问题

一、定义 多个进程因抢夺系统资源而产生相互等待的现象。 二、场景模拟 package com.gui.practise.thread.deadlock;public class DeadLock {private final Object resource1 new Object();//资源 1 private final Object resource2 new Object();//资源 2 public v…

前端架构师之路03_移动端规范兼容处理

1 移动端页面制作规范 1.1 计量单位的使用 CSS 的计量单位选择 px&#xff1a;固定的像素值em&#xff1a;相对父级元素的 font-size 设置来作为当前元素 1em 所代表的像素值&#xff0c;如父节点的 font-size:10px&#xff0c;当前节点的 font-size:1.2em&#xff0c;则当前节…

什么才是物联网领域最好的开发语言?

什么才是物联网领域最好的开发语言&#xff1f; 最好&#xff01;运行最快&#xff1f;开发最高效&#xff1f;最容易学习&#xff1f; 各有特点&#xff01; 采用C/C语言&#xff0c;运行最快&#xff0c;一般采用厂家提供的底层驱动支持包BSP&#xff0c;所有MCU都支持。如…

2009-2018年各省涉农贷款数据(wind)

2009-2018年各省涉农贷款数据&#xff08;wind&#xff09; 1、时间&#xff1a;:209-2018年 2、范围&#xff1a;31省 3、来源&#xff1a;wind 4、指标&#xff1a;涉农贷款 指标解释 &#xff1a;在涉农贷款的分类上&#xff0c;按照城乡地域将涉农贷款分为农村贷款和城…

Spring 学习(五)JavaConfig 实现配置

1. 使用 JavaConfig 实现配置 JavaConfig 是 Spring 项目的一个子项目&#xff0c;Spring 4 后成为核心功能。 注意&#xff1a; 如果开启包扫描&#xff0c;加载配置类以后就可以通过反射拿到配置类中的对象了。Bean 只写在方法上&#xff0c;返回的是一个对象&#xff0c;但…

Django的设计模式及模板层

Django的设计模式及模板层 设计模式MVC和MVT MVC 代表 Model-View-Controller(模型-视图-控制器)模式。 M 模型层(Model),主要用于对数据库层的封装 V 视图层(View),用于向用户展示结果 (WHAT HOW) C 控制(Controller&#xff0c;用于处理请求、获取数据、返回结果(重要) 作…