一次jar包冲突的解决及深入

起因

    因为项目需求,对一个祖传代码进行了GIT迁移及devops环境的迁移,迁移前本地及测试环境启动完全正常,迁移后未做任何代码及环境改变,编译正常通过,但是在启动报错。


Description:

An attempt was made to call a method that does not exist. The attempt was made from the following location:

    javax.el.ELManager.getExpressionFactory(ELManager.java:38)

The following method did not exist:

    javax.el.ELUtil.getExpressionFactory()Ljavax/el/ExpressionFactory;

The method's class, javax.el.ELUtil, is available from the following locations:

    jar:file:/wlfullclient.jar!/javax/el/ELUtil.class

    jar:file:/micoservice/apache-maven-3.6.3/repository/org/glassfish/jakarta.el/3.0.3/jakarta.el-3.0.3.jar!/javax/el/ELUtil.class

The class hierarchy was loaded from the following locations:

    javax.el.ELUtil: file:/src/main/resources/lib/wlfullclient.jar

Action:

Correct the classpath of your application so that it contains a single, compatible version of javax.el.ELUtil

Process finished with exit code 1


错误信息

定位

    从错误信息来看,因为两个jar包都包含了ElUtil类,对比wlfullclient及jakarta中的javax.el相关内容,发现前者代码应该是比较久远的旧版本,找了度娘,wlfullclient包是通过weblogic中的一系列复杂操作(本文不做赘述)打出来的,由于是祖传的代码,不敢对这个包做任何改变T_T。

        度娘找了波资料,感觉该问题其实牵涉到了java及spring boot的classloader相关机制,跟度娘深入沟通良久,猜测问题的根本原因就是wlfullclient中的elutil优先与jakarta的elutil加载。故围绕该原因开始尝试解决。


过程

    根据原因定位,优先考虑采取在pom文件中将spring boot相关的引用中排除wlfullclient包中的java. el包的方案进行尝试,让jakart包使用自己所带的ELUtil类。结果不出所料的失败了,错误异常。

    排除引用失败后,则考虑如何调整两个jar包的加载顺序,想办法让jakart中的elutil类先与wlfullclient包加载,使用idea在项目结构中,在Denpencies将wlfullclient移动到最后,本地启动成功,不再报错。本地解决后,需要考虑打包成jar发布测试环境要如何修改。将maven打的jar包解压后,发现在BOOT-INF文件夹下,有个classpath. idx文件。打开一看,发现其中wlfullclient. jar在文件中的位置要先于jakart. jar。男人的第六感又再告诉我,只要调整了这个文件中的顺序,应该能解决问题。在项目中找了一圈,没有发现任何地方和这个文件有关,最终无奈的打开pom文件想碰碰运气,打开pom文件后发现pom文件中jar包引入的顺序,与classpath. idx文件的顺序大致相同。尝试将wlfullclient包移动到了最后,打包后,classpath. idx文件也变成了想要的顺序。提交代码,通过devops完成发布后,启动正常,问题被解决。



总结

    一个项目启动时,classloader对于每个类的加载都是有一定顺序的,且每个class会且只会被加载一次,不会由于多次引用而被覆盖。本次问题的根本原因就是代码迁移后,由于开发及docker环境发生了变化,导致elutil这个类从原来优先从jakart. jar中加载变成了优先从wlfullclient. jar中加载。度娘查了下这个顺序受应用的配置或者操作系统的文件排序结果影响。相关知识点后需还需延展。

    该问题的解决对于后续jar包冲突时的解决又多了一种新思路,可以通过调整classloader的加载顺序,控制程序加载你想要加载的代码(具体方案还需后续尝试的经验累积)。


牵涉知识点

    java classloader加载原理

    springboot classpath加载原理

    springboot jar包结构及使用

你可能感兴趣的:(一次jar包冲突的解决及深入)