43 多个相同限定名类型同时存在导致的继承结构混乱的情况
Posted 蓝风9
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了43 多个相同限定名类型同时存在导致的继承结构混乱的情况相关的知识,希望对你有一定的参考价值。
前言
// 四刷天府绿道
呵呵 在前面文章中 jetty-runner:jar:9.3.20 和 tomcat-embed-core-8.5.29 的 JarScannerCallback 不兼容, 导致服务启动失败
提到了这样的一个问题
我们再看一下这里的 callback 的接口, jetty-runner 的这个对象里面是没有 void scan(Jar jar, String webappPath, boolean isWebapp), 抛出了异常
当然 假设 jetty-runner 里面 JarScannerCallback 有这个方法, 又会不会出现问题呢?, 我们单开一篇文章讨论
呵呵 这里就是来看这里的问题
当然本文需要对 invokeXX 指令的执行过程有一定的了解, 如下文章 也许有帮助
invoke static/special/virtual/interface
测试用例
UserService 接口
package com.hx.test;
/**
* UserService
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2021-10-24 15:47
*/
public interface UserService
String updateUser(String username, String password);
String removeUser(String username, String password);
测试用例
package com.hx.test;
/**
* Test00MultiClasspathSaveClass
*
* @author Jerry.X.He <970655147@qq.com>
* @version 1.0
* @date 2021-10-24 15:46
*/
public class Test00MultiClasspathSaveClass
// Test00MultiClasspathSaveClass
public static void main(String[] args)
UserService userService = new UserServiceFactory().newUserService();
userService.updateUser("xxx", "xx");
userService.removeUser("xxx", "xx");
另外还需要另外一个包 : UserAddUpdate.jar
里面存放的是 另外的一个 UserService 和 UserServiceImpl
package com.hx.test;
public interface UserService
String addUser(String var1, String var2);
String updateUser(String var1, String var2);
package com.hx.test;
public class UserServiceImpl implements UserService
public UserServiceImpl()
public String addUser(String username, String password)
System.out.println("UserServiceImpl[addUser/updateUser] -> addUser");
return username;
public String updateUser(String username, String password)
System.out.println("UserServiceImpl[addUser/updateUser] -> updateUser");
return username;
package com.hx.test;
public class UserServiceFactory
public UserServiceFactory()
public UserService newUserService()
return new UserServiceImpl();
然后执行 用例, 会发现报错如下
粘贴一下 主要测试用例的字节码信息
master:classes jerry$ javap -v -c com/hx/test/Test00MultiClasspathSaveClass.class
Classfile /Users/jerry/IdeaProjects/HXCase/target/classes/com/hx/test/Test00MultiClasspathSaveClass.class
Last modified Oct 24, 2021; size 800 bytes
MD5 checksum 56f3e1f6bc90419599e855b9992cd33e
Compiled from "Test00MultiClasspathSaveClass.java"
public class com.hx.test.Test00MultiClasspathSaveClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #10.#26 // java/lang/Object."<init>":()V
#2 = Class #27 // com/hx/test/UserServiceFactory
#3 = Methodref #2.#26 // com/hx/test/UserServiceFactory."<init>":()V
#4 = Methodref #2.#28 // com/hx/test/UserServiceFactory.newUserService:()Lcom/hx/test/UserService;
#5 = String #29 // xxx
#6 = String #30 // xx
#7 = InterfaceMethodref #31.#32 // com/hx/test/UserService.updateUser:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#8 = InterfaceMethodref #31.#33 // com/hx/test/UserService.removeUser:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#9 = Class #34 // com/hx/test/Test00MultiClasspathSaveClass
#10 = Class #35 // java/lang/Object
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 LocalVariableTable
#16 = Utf8 this
#17 = Utf8 Lcom/hx/test/Test00MultiClasspathSaveClass;
#18 = Utf8 main
#19 = Utf8 ([Ljava/lang/String;)V
#20 = Utf8 args
#21 = Utf8 [Ljava/lang/String;
#22 = Utf8 userService
#23 = Utf8 Lcom/hx/test/UserService;
#24 = Utf8 SourceFile
#25 = Utf8 Test00MultiClasspathSaveClass.java
#26 = NameAndType #11:#12 // "<init>":()V
#27 = Utf8 com/hx/test/UserServiceFactory
#28 = NameAndType #36:#37 // newUserService:()Lcom/hx/test/UserService;
#29 = Utf8 xxx
#30 = Utf8 xx
#31 = Class #38 // com/hx/test/UserService
#32 = NameAndType #39:#40 // updateUser:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#33 = NameAndType #41:#40 // removeUser:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#34 = Utf8 com/hx/test/Test00MultiClasspathSaveClass
#35 = Utf8 java/lang/Object
#36 = Utf8 newUserService
#37 = Utf8 ()Lcom/hx/test/UserService;
#38 = Utf8 com/hx/test/UserService
#39 = Utf8 updateUser
#40 = Utf8 (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#41 = Utf8 removeUser
public com.hx.test.Test00MultiClasspathSaveClass();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 10: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/hx/test/Test00MultiClasspathSaveClass;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: new #2 // class com/hx/test/UserServiceFactory
3: dup
4: invokespecial #3 // Method com/hx/test/UserServiceFactory."<init>":()V
7: invokevirtual #4 // Method com/hx/test/UserServiceFactory.newUserService:()Lcom/hx/test/UserService;
10: astore_1
11: aload_1
12: ldc #5 // String xxx
14: ldc #6 // String xx
16: invokeinterface #7, 3 // InterfaceMethod com/hx/test/UserService.updateUser:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
21: pop
22: aload_1
23: ldc #5 // String xxx
25: ldc #6 // String xx
27: invokeinterface #8, 3 // InterfaceMethod com/hx/test/UserService.removeUser:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
32: pop
33: return
LineNumberTable:
line 15: 0
line 16: 11
line 17: 22
line 19: 33
LocalVariableTable:
Start Length Slot Name Signature
0 34 0 args [Ljava/lang/String;
11 23 1 userService Lcom/hx/test/UserService;
SourceFile: "Test00MultiClasspathSaveClass.java"
问题的分析
以上用例执行 classpath 为 : /Users/jerry/IdeaProjects/HXCase/target/classes:/Users/jerry/IdeaProjects/HXCase/target/artifacts/UserAddUpdate/UserAddUpdate.jar
大致可以理出程序中使用的 UserService 是 updateUser + removeUser 所在的 UserService, 所以 用例能够正常编译通过
然后 new UserServiceFactory().newUserService() 获取到的实例为 UserServiceImpl, 继承自 addUser + updateUser 所在的 UserService
如果你看过 上面提到的两篇文章, 应该能够想到这个结果 是为什么
Test00MultiClasspathSaveClass 中创建了 UserServiceImpl 的实例, 并调用了 updateUser + removeUser 所在的 UserService 的 updateUser 方法
"invokeinterface #7, 3" 对应的是 Test00MultiClasspathSaveClass 中常量池中第七个元素, 是一个 InterfaceMethodRef
在 invokeinterface 的时候, 发现对应的 cacheEntry 尚未被解析, 于是开始解析 #7, 将符号引用替换为直接引用(解析是通过 name 和 signature 来进行匹配的)
解析 #7 的时候, updateUser + removeUser 所在的 UserService 中有 updateUser(String, String), 并且 UserServiceImpl 中有 updateUser(String, String), 解析成功, 然后调用的是 UserServiceImpl 的 updateUser(String, String)
同理解析 #8 的时候 updateUser + removeUser 所在的 UserService 中有 removeUser(String, String), 并且 UserServiceImpl 中没有有 removeUser(String, String)
然后 vm 的理解是 UserServiceImpl 实现了 updateUser + removeUser 所在的 UserService 的接口, 但是又没有重写 removeUser(String, String) 方法, 抛出了 AbstractMethodError
调整classpath的顺序
假设我们吧 UserAddUpdate.jar 的依赖放在前面, 那么我们 UserService 是 addUser + updateUser 的 UserService, UserServiceImpl 继承自 addUser + updateUser 的 UserService
但是 Test00MultiClasspathSaveClass 中还有一个 removeUser(String, String) 的一个 invokeinterface, 看一下 会有怎么样的效果
invokeinterface #7 和上面同理
同理解析 #8 的时候 addUser + updateUser 所在的 UserService 中没有有 removeUser(String, String)
然后有需要调用 addUser + updateUser 所在的 UserService 的 removeUser(String, String), 因此 直接抛出了 NoSuchMethodError
cmd 中调用 Test00MultiClasspathSaveClass, 调整 classpath 的不同的效果
master:UserAddUpdate jerry$ java -classpath "/Users/jerry/IdeaProjects/HXCase/target/classes:/Users/jerry/IdeaProjects/HXCase/target/artifacts/UserAddUpdate/UserAddUpdate.jar" com.hx.test.Test00MultiClasspathSaveClass
UserServiceImpl[addUser/updateUser] -> updateUser
Exception in thread "main" java.lang.AbstractMethodError: com.hx.test.UserServiceImpl.removeUser(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
at com.hx.test.Test00MultiClasspathSaveClass.main(Test00MultiClasspathSaveClass.java:17)
master:UserAddUpdate jerry$ java -classpath "/Users/jerry/IdeaProjects/HXCase/target/artifacts/UserAddUpdate/UserAddUpdate.jar:/Users/jerry/IdeaProjects/HXCase/target/classes" com.hx.test.Test00MultiClasspathSaveClass
UserServiceImpl[addUser/updateUser] -> updateUser
Exception in thread "main" java.lang.NoSuchMethodError: com.hx.test.UserService.removeUser(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
at com.hx.test.Test00MultiClasspathSaveClass.main(Test00MultiClasspathSaveClass.java:17)
本问题在一般的新项目中不会出现, 但是 在一些老项目, 或者 依赖相当多, 相当杂的情况下 是有可能出现的, 遇到问题的时候不要慌, 看一下本文的理解
参考
invoke static/special/virtual/interface
jetty-runner:jar:9.3.20 和 tomcat-embed-core-8.5.29 的 JarScannerCallback 不兼容, 导致服务启动失败
完
以上是关于43 多个相同限定名类型同时存在导致的继承结构混乱的情况的主要内容,如果未能解决你的问题,请参考以下文章
在C# .net中,多个客户同时写入数据会不会导致数据混乱?
40 classpath中存在多个jar存在同限定名的class classloader会如何加载