关于Jar加载顺序的问题分析

如题所述

第1个回答  2022-07-17
公司在做技术升级,升级日志后遇到应用部署A节点正常,B节点失败诡异情况,而后一段时间再次遇到同类问题,决定有必要一探究竟。
B节点失败的错误信息:

猜测:A节点正常,是因为先加载了sl4j-api.jar中的LocationAwareLogger.class,而B节点失败,是先加载了activemq-all.jar中的LocationAwareLogger.class,启动时程序找不到对应的方法报错。

应用运行在Tomcat容器,翻看类加载部分的源码。slf4j包Logger类加载器是Tomcat的ParallelWebappClassLoader,由父类WebappClassLoaderBase实现类加载功能。
类加载入口:

关注首次class文件从jar包中找到的过程。调用StandardRoot.getResourceInternal寻找class,顺序就是循环allResources(格式:List<List<WebResourceSet>>)。

集合classResources存了WEB-INF/lib目录的Jar资源,在Tomcat启动时调用processWebInfLib()方法初始化。

最终在DirResourceSet类list(String path)方法,其实调用的是java.io.File类list()方法,list调的是UnixFileSystem的native的list()方法。注释及解释;

翻开jdk8对应的OpenJDK源码,UnixFileSystem的list方法,调用的是目录操作函数opendir.

继续向下查操作系统,opendir返回值定义

通过 http://man7.org/linux 关于dirent的排序解释。

命令 ll -f 与opendir函数readdir顺序相同。

继续向下查是文件系统的实现,CentOS 6使用的是Ext4,文件顺序与目录文件的大小是否超过一个磁盘块和文件系统计算的Hash值有关。

因Java语言的跨平台特性,在class首次从jar中找到对应的文件时,查找的顺序是文件操作系统实现决定,与inode值无关。那么像active-all.jar将依赖一起打包的方式极易出现这类问题!!!

感谢前人栽树:

http://man7.org/linux/man-pages/man3/readdir.3.html

https://blog.csdn.net/peter_cloud/article/details/9240317
相似回答