一.背景
应用app依赖consumer.jar,provider.jar,而consumer.jar依赖provider.jar,并且consumer.jar中的Client.java通过接口User.java调用provider.jar的提供的用户信息查询服务(可以是远程服务或本地服务),如果接口的参数由int变为long,而consumer.jar代码没有重新编译,将会出现java.lang.NoSuchMethodError,如果应用程序只做Exception的捕捉,意味整个请求可能就crash了。
二.问题还原
需求:Client调用User接口,查询用户信息,并打印到控制台
服务消费者:consumer.jar用Client.java模拟,Client.java依赖User.java
服务提供者:provider
.jar用User.java和UserImpl.java模拟
1.消费者Client.java
public class Client {
public static void main(String[] args) {
User user = new UserImpl();
user.find(Integer.valueof("123"));
}
}
2.提供者接口User.java
public interface User {
void find(long userId);
}
3.提供者实现UserImpl.java
public class UserImpl implements User {
@Override
public void find(long userId) {
System.out.println("mr.jiang");
}
}
如果接口User将参数类型int变成long,并重新编译打包,提供给应用app使用,而consumer.jar没有重新进行编译,会出现java.lang.NoSuchMethodError:错误,具体分析如下
1.未变更前find(int userId)
编译:javac Client.java ,javac UserImpl.java javac User.java
Client字节码:javap -p -v Client
User字节码:javap -p -v Userpublic static void main(java.lang.String[]); Code: Stack=2, Locals=2, Args_size=1 0: new #2; //class UserImpl 3: dup 4: invokespecial #3; //Method UserImpl."<init>":()V 7: astore_1 8: aload_1 9: bipush 123 11: invokeinterface #4, 2; //InterfaceMethod User.find:(I)V 这里为I,也就是int(映射关系见最后的截图) 16: return LineNumberTable: line 4: 0 line 5: 8 line 6: 16
public interface User
SourceFile: "User.java"
minor version: 0
major version: 50
Constant pool:
const #1 = class #7; // User
const #2 = class #8; // java/lang/Object
const #3 = Asciz find;
const #4 = Asciz (I)V;
const #5 = Asciz SourceFile;
const #6 = Asciz User.java;
const #7 = Asciz User;
const #8 = Asciz java/lang/Object;
{
public abstract void find(int);
}
2.find(int userId)改成find(long userId)
find(int userId)改成find(long userId),然后javac User.java ,javac UserImpl.java,不编译Client.java
Client字节码:javap -p -v Client
public static void main(java.lang.String[]);
Code:
Stack=2, Locals=2, Args_size=1
0: new #2; //class UserImpl
3: dup
4: invokespecial #3; //Method UserImpl."<init>":()V
7: astore_1
8: aload_1
9: bipush 123
11: invokeinterface #4, 2; //InterfaceMethod User.find:(I)V 这里还是I,此时如果运行,则出问题
16: return
LineNumberTable:
line 4: 0
line 5: 8
line 6: 16
User字节码:javap -p -v User
Compiled from "User.java"
public interface User
SourceFile: "User.java"
minor version: 0
major version: 50
Constant pool:
const #1 = class #7; // User
const #2 = class #8; // java/lang/Object
const #3 = Asciz find;
const #4 = Asciz (J)V;
const #5 = Asciz SourceFile;
const #6 = Asciz User.java;
const #7 = Asciz User;
const #8 = Asciz java/lang/Object;
{
public abstract void find(long);
}
运行 java Client 出现error
Exception in thread "main" java.lang.NoSuchMethodError: User.find(I)V
at Client.main(Client.java:5)
3.重新编译Client.java
编译:javac Client
Client字节码:javap -p -c Client
public static void main(java.lang.String[]);
Code:
Stack=3, Locals=2, Args_size=1
0: new #2; //class UserImpl
3: dup
4: invokespecial #3; //Method UserImpl."<init>":()V
7: astore_1
8: aload_1
9: ldc2_w #4; //long 123l
12: invokeinterface #6, 3; //InterfaceMethod User.find:(J)V ==>虽然代码中是123,但是这里“隐式”转成了J,就是long,运行正常
17: return
LineNumberTable:
line 4: 0
line 5: 8
line 6: 17
运行
java Client
控制台输出结果:mr.jiang
三.建议
虽说int,long能够进行装箱和拆箱,但是通过jar包依赖场景下,
被依赖方的修改,必须通知
依赖方进行编译,打包,发布,否则出现问题是error级别的。
附图: