问题:

通过debug发现result.removeAll的时候 删不了

public boolean equals(Object obj) {
return obj instanceof OspSpringBoot2Initializer.OspBean && this.bean.equals(((OspSpringBoot2Initializer.OspBean)obj).bean);
}

this.bean.equals(((OspSpringBoot2Initializer.OspBean)obj).bean 这里返回了false



明明地址一致,那为什么返回了false?

定位问题:

进一步定位。



通过debug定位到: 最终调用java.lang.Object#equals方法的时候,this和obj的引用不一致所以返回false

通过栈帧可以看到:

调用了cglib增强类的equals方法

通过arthas jad 命令反编译出cglib类的代码

public final boolean equals(Object object) {
boolean[] arrbl = SalesNormalPoolService$$EnhancerByCGLIB$$e87bde91.$jacocoInit();
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor != null) {
arrbl[3] = true;
} else {
SalesNormalPoolService$$EnhancerByCGLIB$$e87bde91.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
arrbl[4] = true;
}
if (methodInterceptor != null) {
boolean bl;
Object object2 = methodInterceptor.intercept((Object)this, CGLIB$equals$0$Method, new Object[]{object}, CGLIB$equals$0$Proxy);
if (object2 == null) {
bl = false;
arrbl[5] = true;
} else {
bl = (Boolean)object2;
arrbl[6] = true;
}
arrbl[7] = true;
return bl;
}
arrbl[8] = true;
return super.equals(object);
}

这里结果依赖于methodInterceptor.intercept((Object)this, CGLIB$equals$0$Method, new Object[]{object}, CGLIB$equals$0$Proxy);

我们继续看下intercept 这个方法

public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
//.. 省略 不会走这里的
}



发现是通过反射调用java.lang.Object#equals方法,此时是this.equals(args[0]) 发现地址不一致 结果返回false。

所以bug就定位到

method.invoke(this, args);

解决:

正确写法应该是:

methodProxy.invokeSuper(o, args);

public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return methodProxy.invokeSuper(o, args);
}
//.. 省略 不会走这里的
}

修改过后



在调用java.lang.Object#equals时



传入了o 和 args[0] ,它们两个地址相同,所以返回true

我们继续看下栈帧:



现在有调用CGLIB$equals$0:-1, SalesNormalPoolService$$EnhancerByCGLIB$$e87bde91

final boolean CGLIB$equals$0(Object object) {
boolean[] arrbl = SalesNormalPoolService$$EnhancerByCGLIB$$e87bde91.$jacocoInit();
arrbl[2] = true;
return super.equals(object);
}





也就是 现在的调用是:o == args[0] → true

以前的调用是:this == args[0] → false

cglib 代理类自己equals自己 返回false问题的更多相关文章

  1. spring查看生成的cglib代理类源码详解

    1.让程序阻塞(抛出异常会导致程序结束,所以在抛出异常之前阻塞) 2. windows控制台 cd到jdk目录下的lib目录,找到sa-jdi.jar 执行: java -classpath sa-j ...

  2. 从数据库取出两个同样的字符串用equals比较返回false

    1. 从网上搜索原因,大概总结为三点 1.1 取数据的两个数据库编码不一样,需要统一编码 1.2 字符类型不一样,可能一个为nchar一个为varchar 1.3 从数据库取出的数据有空格,需要tri ...

  3. JDK动态代理和cglib代理

    写一个简单的测试用例,Pig实现了Shout接口 public class MyInvocation implements InvocationHandler { Object k; public M ...

  4. Spring框架系列(11) - Spring AOP实现原理详解之Cglib代理实现

    我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理.@pdai Spring框架系列 ...

  5. 关于Object类的equals方法和hashCode方法

    关于Object类的equals的特点,对于非空引用: 1.自反性:x.equals(x) return true : 2.对称性:x.equals(y)为true,那么y.equals(x)也为tr ...

  6. 分享知识-快乐自己:三种代理(静态、JDK、CGlib 代理)

    1):代理模式(静态代理)点我下载三种模式源码 代理模式是常用设计模式的一种,我们在软件设计时常用的代理一般是指静态代理,也就是在代码中显式指定的代理. 静态代理由 业务实现类.业务代理类 两部分组成 ...

  7. Java代理模式精讲之静态代理,动态代理,CGLib代理

    代理(Proxy)是一种设计模式,通俗的讲就是通过别人达到自己不可告人的目的(玩笑). 如图: 代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象 这三个代理模式,就 ...

  8. java动态代理类

    很有意思的一个东西,在java.lang.reflect包下 示例代码 package com.guangshan.test.proxy; import java.lang.reflect.Invoc ...

  9. CGLIB 和 JDK生成动态代理类的区别(转)

    文章转自http://luyuanliang.iteye.com/blog/1137292 AOP 使用的设计模式就是代理模式,是对IOC设计的补充.为了扩展性,往往会加上反射,动态生成字节码,生成代 ...

  10. JDK和CGLIB生成动态代理类的区别

     关于动态代理和静态代理 当一个对象(客户端)不能或者不想直接引用另一个对象(目标对象),这时可以应用代理模式在这两者之间构建一个桥梁--代理对象. 按照代理对象的创建时期不同,可以分为两种: 静态代 ...

随机推荐

  1. java重载-构造方法也存在重载-数据类型的提升

    重载 1.一个类中不能声明多个相同的方法,属性. 2.上面的相同指的是方法名,参数列表相同.和返回值类型无关. 3.如果方法名相同,但是参数列表(个数,顺序,类型)不相同,会认为是不同的方法,在jav ...

  2. NAT原理:概念、使用场景、转发流程及规则

    本文分享自天翼云开发者社区<NAT原理:概念.使用场景.转发流程及规则>,作者:x****n 网络地址转换(NAT)是一种在计算机网络中将一个网络的IP地址转换为另一个网络的IP地址的技术 ...

  3. Hetao P1391 操作序列 题解 [ 绿 ] [ 二维线性 dp ]

    操作序列:简单的二维 dp. 观察 我们每次操作可以让 \(x\) 变为 \(2x-1\),或者当 \(x\) 为奇数时让 \(x\) 变为 \(\frac{x+1}{2}\). 显然,执行第一种操作 ...

  4. 你的边比较松弛:最短路的 Bellman-Ford 和 SPFA 方法

    Dijkstra 的局限性 在带权图的最短路径问题中,我们的目标是从一个起点出发,找到到达其他所有节点的最短路径.无论是交通导航中的最短耗时路线,还是金融网络中的最小成本路径,这一问题的核心始终是如何 ...

  5. C# 心跳检测实现

    原文链接: https://blog.csdn.net/yupu56/article/details/72356700 TCP网络长连接 手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以 ...

  6. kubsphere应用系列(三)-创建手动流水线

    准备工作 1.1 创建凭证 1.2 添加代码仓库 第一步创建流水线   第二步配置流水线 1.1选择CI/CD模板 1.2删除多余阶段 1.3 配置git仓库信息     1.4配置docker仓库信 ...

  7. 交叉编译SQLite3

    交叉编译SQLite3 SQLite是一个进程内的库,实现了自给自足的.无服务器的.零配置的.事务性的SQL 数据库引擎. 它是一个零配置的数据库,这意味着与其他数据库不一样,您不需要在系统中配置. ...

  8. linux命令行连接wifi

    linux命令行连接wifi 1.安装nmcli sudo apt-get install nmcli 2.查看网络设备 sudo nmcli dev 3.开启wifi sudo nmcli r wi ...

  9. startup_stm32f10x_xx.s 启动代码文件的选择

    网上查到的各个文件的解释是: startup_stm32f10x_cl.s 互联型的器件startup_stm32f10x_hd.s 大容量startup_stm32f10x_hd_vl.s 大容量s ...

  10. 解释 Git 的基本概念和使用方式

    Git是一种分布式版本控制系统,常用于管理和追踪软件开发项目的代码.以下是Git的基本概念和使用方式的解释: 仓库(Repository):Git管理代码的基本单位,可以理解为一个存储代码历史和版本信 ...