有一个罐子有2个版本 在版本1中,主要有2个类
Class A
Class B
Class A has a constructor as
A(X,B)
在版本2中,主要有2个类和一个接口
Class A
Interface C
Class B implements C
Class A has a constructor as
A(X,C)
在我的util类中,我调用了A
的构造函数A a= new A(x,new B());
上面的代码是使用jar 1构建的ant构建正常的。
现在在生产环境中,类路径中的jar是版本2。 我收到错误
NoSuchMethodError A.<init>(x;B)
无法理解在版本2中,jar B是C的实现,为什么会出现这个错误? 请帮我理解
答案 0 :(得分:2)
这是因为您正在更改构造函数的函数签名。如果您检查Util
类的字节码,您将看到它正在使用invokespecial
,而不是invokevirtual
操作码(构造函数和私有方法通过invokespecial
调用)。 invokespecial
在某种意义上是非常特殊的,它可以进行静态绑定。对于更长的解释,我建议阅读:article
Invokespecial与invokevirtual主要不同之处在于 invokespecial根据引用的类型选择方法 而不是对象的类。换句话说,它确实是静态的 绑定而不是动态绑定。在三种情况中的每一种情况下 在使用invokespecial的情况下,动态绑定不会产生 期望的结果。
通过编译后进行的版本更改,您实际上是擦除了Util类可以调用的唯一构造函数,而且这次不能依赖动态绑定。
答案 1 :(得分:1)
我的赌注是版本1仍然潜伏在您的生产类路径中的某个位置以及版本2,它是您的类加载器遇到的第一个。
事实上你应该把它视为一种祝福,因为否则你会有一个小小的无声虫,它永远不会造成严重破坏,直到将来你的类加载器给你v1的某个随机时间(发生在我以前的公司并且生产进入停止状态)整整一周)。
这通常很难调试,因为版本1可以由另一个jar,war,容器,maven传递,意外地打包jar / war,osgi bundle或者更糟:由一个流氓类加载器(好运)注入用这个)
如果它让你永远解决这个问题,我会将版本2重命名为A2,如果可以的话,它会有确定的不同名称(是的,这是一个丑陋的解决方案)。
另一个常见原因是JBoss在多次重新部署之间没有清理自己。因此,当您部署应用程序版本567时,类路径仍然具有版本566的垃圾。通常,这可以通过执行干净部署(终止JBoss进程,删除临时文件夹,再次启动它)来解决。
答案 2 :(得分:0)
当代码与version1 jar一起编译时,然后使用带有参数作为类文件(类B)的构造函数生成类文件,并且它没有接口C. 当同一个jar被移动到生产环境时,构造函数有接口C(类型C),它与类B(类型B)不匹配,因此出现了nosuchmethoderror的错误。当代码符合版本2 jar时,错误得到解决。< / p>