查看原文
其他

玩大发了,Tomcat 8.5 升级有坑…

栈长 Java技术栈 2021-01-12

Java技术栈

www.javastack.cn

关注阅读更多优质文章



最近某个系统做了环境升级:

  • 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技术栈公众号往期文章找到相对应的解答。

最近热文:
1、Spring Boot 干掉了 Maven 拥抱 Gradle!
2、一周面试了 30 人,面到我心态爆炸…
3、阿里为什么不用 Zookeeper 做服务发现?
4、写了个全局变量的bug,被同事们打脸!
5、Java 14 祭出神器,Lombok 被干掉了?
6、为什么 Redis 单线程能达到百万+QPS?
7、Spring Boot 2.3 优雅关闭新姿势,真香!
8、Redis 到底是单线程还是多线程?
9、我天!xx.equals(null) 是什么骚操作??
10、Spring Boot 2.3.1 发布, 10 个新特性!
扫码关注Java技术栈公众号阅读更多干货。

点击「阅读原文」获取面试题大全~

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存