Tomcat NoSuchMethodError到底是怎么发生的

Tomcat NoSuchMethodError到底是怎么发生的背景 业务从ECS在迁移到容器之后,Tomcat从7升级到了8.5,开始频繁出现NoSuchMethodError. 并向我反馈以前不会。期待我可以给出一个结论 在查阅资料和研究其源代码之后,我发现了

背景

业务从ECS在迁移到容器之后,Tomcat从7升级到了8.5,开始频繁出现NoSuchMethodError. 并向我反馈以前不会。期待我可以给出一个结论

在查阅资料和研究其源代码之后,我发现了如下区别,并稳定的复现了 jar包冲突 的情况下 必然出现 该错误和 必然不出现 该错误。

Tomcat7和Tomcat8加载的区别和相同点

Tomcat7和8 Classloader说明文档

先说结论

加载class的方式都是一样的,都是读取classpath下的jar包的class文件数据,但由于lib的排序方式发生了变化,导致读取class发生了不一致. 从而引发NoSuchMethodError。

相同点

1、读取class的方式,伪代码如下:

//jarFiles ===> WEB-INF/lib 下的lib文件
for (i = 0; (entry == null) && (i < jarFilesLength); i++) {
	jarEntry = jarFiles[i].getJarEntry(jarEntryPath);
  if (jarEntry != null) {
  	//tomcat biz
    //返回 ResourceEntry
  }
}

谁先找到class文件谁先加载,问题就出现在jarFiles的排列顺序 jarFiles的是直接读取的WEB-INF/lib下的文件,那么读取的顺序就决定了加载的顺序

Tomcat7 读取顺序代码如下: FileDirContext.java (在Tomcat8中被重构掉)

protected List<NamingEntry> list(File file) {
    String[] names = file.list();
    Arrays.sort(names);//这里进行了一次排序,得出的结果和我们在WEB-INF/lib下执行ll命令一致
    //剩下的省略掉
}

Tomcat8 读取顺序代码如下:DirResourceSet.java

 @Override
 public String[] list(String path) {
     String webAppMount = getWebAppMount();
     if (path.startsWith(webAppMount)) {
         File f = file(path.substring(webAppMount.length()), true);
         if (f == null) {
             return EMPTY_STRING_ARRAY;
         }
         //读取文件,然而没有了排序. 完全无序,具体的读取全看native的实现,这里我不清楚
         String[] result = f.list(); 
         if (result == null) {
             return EMPTY_STRING_ARRAY;
         } else {
             return result;
         }
     } else {
     		//省略部分
         return EMPTY_STRING_ARRAY;
     }
 }

既然没有了顺序,那么我们就不知道他会从哪一个jar中读取,但是还是有个规律的,如果n次发布的jar包都没有变化,那么n从读取的顺序也是没有变化的. 

从上述结论定制NoSuchMethodError

1、打包jar包3份,分别是

-rw-r--r-- 1 admin wheel 46K 2 16 08:19 safe-common-0.1.0-SNAPSHOT.jar 1
-rw-r--r-- 1 admin wheel 46K 2 16 08:08 safe-common-1.0-SNAPSHOT.jar 2
-rw-r--r-- 1 admin wheel 46K 2 15 21:37 safe-common-2.0-SNAPSHOT.jar 3

1和3 代码一致,比2多出一个方法. 

public class NoSuchMethodTest {
    public NoSuchMethodTest() {
    }
    //jar包1,2,3中都有
    public static void handle(String test) {
        System.out.println("handle===>" + test);
    }
	 //jar包1,3中才有
    public static void handle(String test, String test2) {
        System.out.println("2.0===>handle===>" + test + ":" + test2);
    }
}

调用代码

@RequestMapping("zz")
@ResponseBody
public String xx() {
    try {
        NoSuchMethodTest.handle("123", "123");
    } catch (Throwable e) {
        return e.getMessage();
    }
    return "zz";
}

测试1

将2和3直接放到WEB-INF/lib下,调用

调用结果如下,必然发生NoSuchMethodError

➜  lib curl   127.0.0.1:18080/zz.json com.raycloud.safe.group.api.such.NoSuchMethodTest.handle(Ljava/lang/String;Ljava/lang/String;)V%

测试2 

将1、2、3号jar包都放入,因为在tomcat7下排列顺序为为1、2、3号jar包依次排列,所以没有问题

在而Tomcat8下则跟file.list的读取顺序有关. 

其他链接

bz.apache.org/bugzilla/sh…

github.com/openwide-ja…

总结

世界是科学的

今天的文章Tomcat NoSuchMethodError到底是怎么发生的分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/16046.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注