最近某全系统做了环境升级:
- Tomcat 8.5.x
- JDK 1.8.x
有个系统升级后出现没有这个方法异常:
threw exception; nested exception is java.lang.NoSuchMethodError: ... ...
上线后系统起不来,这下玩大了。。。
咋一看应该是 jar 包冲突了,经过排查,果然是 jar 包冲突了,类路径下存在了几个不同版本类似的 jar 包,是由另外一个依赖引入进来的 compile 依赖,然后排除冲突的依赖就解决了。
那么问题来了,既然 jar 包冲突,相同的环境,为什么在开发环境、测试环境、预上线环境都没有出现问题?
经过查证,那是因为 Tomcat 8 之后的 jar 包加载方式变了,8 之前是按照字母顺序加载的,而 8 之后就变了,Tomcat 使用的是 File.listFiles() 方法来获取目前下的 jar 包,加载的顺序就和文件系统返回的顺序有关,就不一定是按字母排序了。
java.io.File#listFiles() 源码注释:
这个方法是获取目前下的所有文件,它不能保证按指定的顺序返回结果,特别是,不能保证按字母顺序返回。为什么本地环境可以,可能是刚好是按字母排序的了。
那么,这个问题的解决办法可以是:
1)如果是冲突问题,排除冲突依赖解决 jar 包冲突或者降级到 tomcat 7 就行了,又或者是写个脚本检查一下包名是否有重复的包;我们刚好是有两个 jar 包冲突了,解决冲突就正常了;
2)如果是业务问题,我们都知道 JVM 双亲委派机制,同一个类是不能重复加载的,如果业务确实需要加载多个版本的依赖,那就需要实现自己的自定义类加载器分别去加载对应的 jar 包;
关于类加载器、双亲委派机制都可以在Java技术栈公众号往期文章找到相对应的解答。