bug现场谜之困在“init”方法上的那些时间!

bug现场谜之困在“init”方法上的那些时间!

💡 原文中文,约4900字,阅读约需12分钟。
📝

内容提要

在将Tomcat 8.5的war包迁移到Jetty 9.4.19时,出现NoSuchMethodError错误,表明运行时找不到对应的方法。这通常是由于编译时和运行时依赖不一致造成的。尽管使用javap工具确认类存在构造方法,但Jetty版本升级导致方法定义变化。开发者需注意普通方法与构造方法的区别,以避免类似问题。

🔎

延伸解读

NoSuchMethodError 的常见诱因

NoSuchMethodError 通常意味着编译时与运行时依赖的类不一致。编译时 CLASSPATH 中的类包含某方法,但 JVM 运行时加载的类可能来自不同版本的 jar 包,导致方法缺失。对于同一 ClassLoader,一个类只会被加载一次,因此若 classpath 中存在多个同名类,实际加载哪个取决于平台,Linux 和 Windows 可能不同。

定位加载类的实用方法

要确定报错类从哪个 jar 加载,可选用三种方式:启动 JVM 时添加 -verbose:class 参数,在日志中查找类加载路径;通过代码获取类的 ProtectionDomain 和 CodeSource 得到位置;或在 Linux 下用 jar tvf 遍历 jar 包搜索类文件。对于 war 包,依赖可能来自 WEB-INF/lib 或容器 lib 目录,需注意 classpath 范围。

普通方法与构造方法的区别

异常信息中 .init() 表示普通方法调用,而 .<init>() 表示构造方法调用。本文案例中,报错为 WebSocketServerFactory.init(ServletContext),但实际类中只有构造方法 WebSocketServerFactory(ServletContext)。Jetty 从 9.3 升级到 9.4 时,将 init 由普通方法改为构造方法,导致迁移后出现 NoSuchMethodError。

迁移时的依赖版本检查

将应用从 Tomcat 迁移到 Jetty 时,需注意容器版本升级可能改变 API 定义。即使 javap 显示方法存在,也要区分是普通方法还是构造方法。建议在迁移前检查关键依赖的版本变更日志,并在运行时确认实际加载的类版本,避免因方法签名变化引发 NoSuchMethodError。

❓

Q&A

在迁移war包时遇到NoSuchMethodError错误的原因是什么?

NoSuchMethodError错误通常是由于编译时和运行时依赖不一致造成的。

如何确认类的构造方法是否存在?

可以使用javap工具确认类的构造方法是否存在。

Jetty 9.4与9.3版本在WebSocketServerFactory的init方法上有什么变化?

Jetty在9.3升级到9.4时,将WebSocketServerFactory的init方法由普通方法改为构造方法。

如何定位JVM加载的类的jar包?

可以使用JVM参数-verbose:class输出加载class的jar包绝对路径,或通过Java代码获取类的CodeSource。

普通方法和构造方法在异常堆栈信息中如何区分?

普通方法用.init()表示,构造方法用.<init>()表示。

在开发中如何避免NoSuchMethodError错误?

开发者需注意编译时和运行时依赖的一致性,确保方法定义未发生变化。

🏷️

标签

➡️

继续阅读