文章目录
背景
在新代码中调用的高版本方法但是依赖的历史版本用的低版本,存在调用方法不存在,有可能你编译时通过,但是调用时发现JVM依赖的版本是旧版本,导致调用的新版方法发生异常。例如我下面的这个异常,那么我们就需要调和这种依赖冲突。
java.lang.NoSuchMethodError: 'java.lang.reflect.Field[] org.apache.commons.lang3.reflect.FieldUtils.getAllFields(java.lang.Class)
'
xxxx.core.domain.SplitFields(25)
这种情况通常是我们依赖第三方jar,或者其他团队提供的jar,自身没有源码,或者自己的历史版本源码实在不想动了。
或者我们希望在现有的jar中添加自己的方法,或者修改现有jar中的依赖启动项目,比如历史jar依赖的当前运行环境中的resource中的配置文件,或者特定的文件路径比如d//config,这种如果在centos环境,或者在docer环境根本就无法加载到这些文件路径。因此需要我们手动修改源文件,但是有没有源码的情况下的不得已而为之。
三种修改方式
1.POM中移除原jar中依赖的历史版本
如果原jar使用的pom.xml依赖我们可以使用exclusions排除该依赖,当然也就不需要我们这里提到的修改原jar了
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
2.原jar它不使用pom依赖而是直接放在源码中再编译
这种情况使用上面的排除是没办法的,如下图,org.apache.commons.lang3的源码它给直接合到它的项目中了,而不是使用的依赖pom加载,这种方式,你只能通过本文的方式手工清理这种强依赖。
方法如下:
-
使用rar解压工具打开文件,找到对应的org.apache.commons.lang3,直接删除即可
-
切换到META-INF\maven下找到
找到对应的commons-lang3直接删除
如此重新添加到仓库中或者放入项目的lib下面即可解除旧版本的依赖
使用JarEditor 插件对源码进行修改(推荐)
它让你无需再用其他第三方工具,也无需解压 JAR 文件就能直接在IDEA中编辑其中的类和资源文件。还能用它轻松添加、删除或重命名 JAR 包里的文件。即使遇到混淆过的 JAR,JarEditor 也能通过 Javassist 工具搞定
JarEditor 提供了丰富的功能,尤其是对于开发者日常操作 JAR 文件时非常有用的特性:
- 直接编辑 JAR 包内的类和资源文件:无需将 JAR 文件解压缩,支持对其中的类(.class 文件)和资源文件直接进行编辑。
- 添加、删除或重命名 JAR 包内的文件和文件夹:简化了对 JAR 包内容的管理。
- 搜索 JAR 包的内容:通过关键字快速定位 JAR 包中的文件或类。
- JAR 内部文件的复制与粘贴:支持将 JAR 包中的文件复制到外部剪贴板,也支持从剪贴板粘贴文件到 JAR 包内。
- 支持 SpringBoot JAR:可以处理 SpringBoot 打包的 JAR 文件。
- 支持 Kotlin:除了 Java,JarEditor 也支持 Kotlin 的类和文件操作。
- 导出 source JAR:可以直接将 JAR 文件的源码导出为 source JAR。
- 字节码修改工具:通过集成的 Javassist 工具,支持直接修改 .class 文件的字节码。
IDE安装完 JarEditor插件后,右键点击项目依赖jar中对应的的 .class 文件,在反编译界面可以切换到 JarEditor 的 tab 页面,方便对 JAR 文件进行编辑。
- pom加载jar : pom中依赖的jar
- 磁盘加载jar: 对于外部 JAR 文件,可以通过 File -> Project Structure -> Libraries -> Add Library 来添加外部 JAR,并在项目视图中对其进行反编译和编辑操作
如下图我对我遇到的依赖jar中的旧版本代码进行修改增加新版本中新的方法如下图
保存修改后的文件方式:
1.保存源码:保存文件到jar对应磁盘同级别目录下的反编译源码;target选择JDK版本8即可->save
2.保存编译后的文件重新打包为jar:target选择JDK版本8->Build jar ;
编译后这个jar就是我们引入的jar会被覆盖,编译完我们就可以直接把这个jar传到私有仓库方便下次使用
保存后我们关闭编辑页面,重新双击打开jar中的FieldUtils我们可以看到它已经有我们上面添加的方法代码块了。
请看演示动画[下图动画来自引用文章]
使用java-decompiler反编译后修改源码覆盖原class(不好用-不推荐直接跳过)
以上都不行那就只能修改原class了,比如写死的加载路径,或者已经无法再外面满足的配置文件地址等等。
jar解压jar拿到源class先看通用命令
#解压当前文件到当前目录
jar -xvf .\MQSDK1.6.1.jar
#重新压缩当前路径下的所有文件为 MQSDK1.6.1.jar
jar cvfM0 MQSDK1.6.1.jar ./*
比如这里我们看到了,需要修改这个代码rg.git.gr.modules.clm.controller.company.CompanyApplyController
首先创建或者直接使用现有的项目,在src下面新建一个同名同目录的类文件org.git.gr.modules.clm.controller.company.CompanyApplyController目的是编译后后它的包路径和文件名方法名依然和原来一样,不会造成代码冲突和异常
然后使用java-decompiler反编译工具找到原jar解压路径种对应类,复制里面的内容信息到自己新增的类中,修改新类的源码后重新编译,使用编译后的class文件覆盖原解压的jar中的对应文件。
提醒
以上修改原jar的方式,如果使用的pom加载记得都要修改版本号后提交到仓库中,不然下次拉取可能还是原来的版本内容。
如果使用 外部加载lib的方式则保留好修改后的jar资源就可以了。
参考资料-推荐阅读拓展
告别繁琐反编译:IDEA中轻松反编译与修改Jar包
https://datamining.blog.youkuaiyun.com/article/details/142311328