欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

增量发布的NoSuchMethodError 异常 博客分类: 虚拟机、编译器、增量发布 增量发布javap.NoSuchMethodError 

程序员文章站 2024-03-23 11:48:52
...
NoSuchMethodError多是因为类冲突,没有加载版本需要的class而加载了其他路径下的class,但是本文记录的是重载代码修改在增量发布环境下出现的异常情况!
调用者:
package util;
 
public class Invoker {
public static void main(String[] args) {
CommUtil c=new CommUtil();
String s="ssssssssss";
System.out.println(c.format(s));
}
}
 
报异常:
Exception in thread "main" java.lang.NoSuchMethodError: util.CommUtil.format(L
va/lang/String;)Ljava/lang/String;
at util.Invoker.main(Invoker.java:9)
 
 
原因是因为重载方法删除后,调用者的的代码没有重新编译。该问题出现在“增量发布”的场景,只发布了代码修改过的CommUtil.class,没有发布Invoker的class
 
 
当CommUtil.java 中有如下两个重载方法时
package util;
 
public class CommUtil {
 
public String format(String v ){
 
return "String format "+v;
}
 
public String format(Object v){
return "Object format "+v.toString();
}
 
}
 
调用类编译后class的反编译后如下:
 
D:\clstest\util>javap -c Invoker.class
Compiled from "Invoker.java"
public class util.Invoker {
public util.Invoker();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":
()V
4: return
 
public static void main(java.lang.String[]);
Code:
0: new #16 // class util/CommUtil
3: dup
4: invokespecial #18 // Method util/CommUtil."<init>":()V
 
7: astore_1
8: ldc #19 // String ssssssssss
10: astore_2
11: getstatic #21 // Field java/lang/System.out:Ljava/
io/PrintStream;
14: aload_1
15: aload_2
16: invokevirtual #27 // Method util/CommUtil.format:(Ljav
a/lang/String;)Ljava/lang/String;
19: invokevirtual #31 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
22: return
}
 
当删除format(String v) 这个方法后,CommUtill.java内容如下:
 
package util;
public class CommUtil {
 
public String format(Object v){
return "Object format "+v.toString();
}
 
}
 
javap -c 反编译如下:
D:\src\ssm3\target\test-classes\util>javap -c Invoker.class
Compiled from "Invoker.java"
public class util.Invoker {
public util.Invoker();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":
()V
4: return
 
public static void main(java.lang.String[]);
Code:
0: new #16 // class util/CommUtil
3: dup
4: invokespecial #18 // Method util/CommUtil."<init>":()V
 
7: astore_1
8: ldc #19 // String ssssssssss
10: astore_2
11: getstatic #21 // Field java/lang/System.out:Ljava/
io/PrintStream;
14: aload_1
15: aload_2
16: invokevirtual #27 // Method util/CommUtil.format:(Ljav
a/lang/Object;)Ljava/lang/String;
19: invokevirtual #31 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
22: return
}
 
 
总结:
 
重载是编译是的多态行为,java编译器会根据多态的方法的参数就近选择一个方法编译。如上面例子,当CommUtil 有format(String v )方法时候,Invoker 调用format时会被编译成
 16: invokevirtual #27 // Method util/CommUtil.format:(Ljav
a/lang/String;)Ljava/lang/String;
 
但是当删除format(String v )只留下format(Object v )时,会被编译成
 16: invokevirtual #27 // Method util/CommUtil.format:(Ljav
a/lang/Object;)Ljava/lang/String;
 
所以在生成环境的增量发布流程需要优化,需要注意重载代码修改后的相关优化或者谨慎对重载方法进行修改