int修改为long的悲剧

来源:互联网 发布:淘宝网店策划书 编辑:程序博客网 时间:2024/04/30 11:56

一.背景

     应用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
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:bipush123   11:invokeinterface#4,  2; //InterfaceMethod User.find:(I)V  这里为I,也就是int(映射关系见最后的截图)   16:return  LineNumberTable:    line 4: 0   line 5: 8   line 6: 16
User字节码:javap -p -v User
public interface User  SourceFile: "User.java"  minor version: 0  major version: 50  Constant pool:const #1 = class#7;//  Userconst #2 = class#8;//  java/lang/Objectconst #3 = Ascizfind;const #4 = Asciz(I)V;const #5 = AscizSourceFile;const #6 = AscizUser.java;const #7 = AscizUser;const #8 = Ascizjava/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:bipush123   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;//  Userconst #2 = class#8;//  java/lang/Objectconst #3 = Ascizfind;const #4 = Asciz(J)V;const #5 = AscizSourceFile;const #6 = AscizUser.java;const #7 = AscizUser;const #8 = Ascizjava/lang/Object;{public abstract void find(long);}

运行 java Client 出现error
Exception in thread "main" java.lang.NoSuchMethodError: User.find(I)Vat 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级别的。


附图:



0 0
原创粉丝点击