Java项目中如何同时使用两个版本的jar?
1.背景
项目里已经引入了bcprov-jdk15on-1.54.jar
,并且在很多地方使用.
现在合作方给过来一套新的接入资料,里面有以下jar包.
bcprov-jdk15on-1.69.jarfintech-java-sdk-core-3.1.0.jar
fintech-java-sdk-kdft-3.1.0.jar
2.问题
项目中的bcprov-jdk15on存在两个版本,旧的是1.54
,新的是1.69
.
类似于下面这张图:
由于很多地方用到了1.54
版本,所以不能直接升级到1.69
.
现在想实现以下两点:
- 对于本次的新版本,其中的fintech-java-sdk-core-3.1.0.jar、fintech-java-sdk-kdft-3.1.0.jar需要使用到bcprov-jdk15on-1.69.jar
- 对于以前的版本,bcprov-jdk15on-1.54.jar保持不动,还是使用这个版本.
即本次的升级不会影响到历史功能.
3.我的分析
如果不考虑冲突的情况,正常使用新jar,示例代码如下:
DefaultKdClient defaultKdClient = KdSdkConfig.getDefaultClient();DemoApiInfo demoApiInfo = new DemoApiInfo();
Map<String, String> params = new HashMap<>();
DefaultKdRequest<Object> kdRequest = new DefaultKdRequest<>(demoApiInfo,
params);
我想过使用类加载器,可能是理解的还不到位,我觉得每个类都这样引入比较麻烦,比如DefaultKdClient
// custom class loader: CustomClassLoaderCustomClassLoader classLoader = new CustomClassLoader();
Class<?> defaultKdClient = classLoader.loadClass("xx.DefaultKdClient");
而且我不太确定这样能确保fintech-java-sdk-core-3.1.0.jar、fintech-java-sdk-kdft-3.1.0.jar中依赖的bcprov-jdk15on能是1.69版本.
除此之外,我也想过新建一个项目,这样就可以有不同的运行时,使得bcprov-jdk15on不再冲突,但是新项目需要新的维护成本,这种方式感觉也不太好.
4.问题
上面的场景,请问有什么比较好的解决方案,谢谢~
回答:
使用自定义类加载器能解决你的问题,例子。
这里给的例子只是一种最简单的解决方式,但是它肯定不适合你的情况,因为在你的代码中,调用一个 jar 肯定是直接通过类名调用的,而例子中给的只是反射。
因此需要你再针对 jar 的方法包装一下,比如你可以新建一个类 Bcprov_1_54
,加载 bcprov-jdk15on-1.54.jar
,代理你用到的方法,然后再新建一个类 Bcprov_1_69
,加载 bcprov-jdk15on-1.69.jar
。
这样你就拥有了两个版本的调用方式。
回答:
虽然题目已经排除了。
但我始终觉得,升级把报错的地方都改了才是最佳的做法
回答:
新包改个包名 打包引入
这里有别人给出的两种方案
https://cloud.tencent.com/developer/article/1926072
回答:
如果上面jar 包都有源码的话,可以对基中一个版本整体通过包名重构,这样就可以解决冲突,需要的测试和维护成本较低。
如果可能的话,还是建议将其低版本升级,否则发现新的漏洞就只能自己维护了。
回答:
可以新建一个最简单的maven项目,通过maven-shade-plugin
插件,把老版本groupId重命名后打个包,配置大概如下(注意看注释部分的重命名配置)
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>shade-hutool57</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.7.22</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<relocations>
<relocation>
<!-- 原始groupId -->
<pattern>cn.hutool</pattern>
<!-- 重命名groupId -->
<shadedPattern>cn.hutool.charles</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
然后拷贝target里的xxx.jar,在正式项目里添加依赖,配置如下
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>hutool-multi-version</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.8.20</version>
</dependency>
<dependency>
<groupId>cn.hutool.v57</groupId>
<artifactId>hutool-core</artifactId>
<version>5.7.22</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/shade-hutool57-1.0-SNAPSHOT.jar</systemPath>
</dependency>
</dependencies>
</project>
使用的时候,import新版本还是老版本需要自己决定,Java import无法别名,一个类不能同时用新老版本
package org.example;//import cn.hutool.core.util.RandomUtil;
import cn.hutool.charles.core.util.RandomUtil;
public class Main {
public static void main(String[] args) {
System.out.println(RandomUtil.randomString(32));
}
}
回答:
有个思路,如果有源码可以借助Javaparser之类的语法分析工具,把bcprov-jdk15on-1.69.jar这个包中类的包名都加一层,把fintech-java-sdk-core-3.1.0.jar和fintech-java-sdk-kdft-3.1.0.jar中的import改一下。
我搜索发现有一个jarjar.jar可以实现单个jar包的修改,但是关联修改fintech-java-sdk-core-3.1.0.jar和fintech-java-sdk-kdft-3.1.0.jar中导入的功能好像没现成的。可以参考这个
回答:
bourcycastel的版本冲突比较常见,
针对你的要求,可以通过重新构建jar包的方式,把1.54的版本构建为一个新的jar包,将里面的package的路径和1.69的进行区分,例如把org.bourcycastel 全都改成 com.bourcycastel。
这样在使用同名类的时候,就可以通过不同的import来区分来自哪个版本的jar包。关于重新构建的方法,有一个java打包工具,叫jarjar,通过命令行配置规则,可以实现上述目标,也可以通过maven的shade plug。
不过,问题的本质不是jar包冲突,是需要在使用的时候区分引用。
如果bourcy castel是作为间接使用的,比如另一个依赖abc.jar,它也依赖bc的1.54版本,那意味着重新构建修改包路径之后,这个abc中的所有相关import,也需要修改,否则他会指向1.69版本的org.bourcycastel,而在新版本中,相同方法可能没有变化,可能删除了,可能改变参数和返回了,那这种影响是很难测试的,很可能只在特定情况下会暴露出来,直接造成生产事故。
所以最建议的做法,先通过mvn依赖分析,找出所有依赖1.54的地方,然后一个一个的判断,和1.69做比较,进行版本升级改造,虽然麻烦一点,但是更可靠。改造过程中,自己代码中使用的地方,通过工具类,对第三方依赖方法的抽取封装,实现后续的升级扩展能力,对于其他间接依赖,优先查找依赖的最新版本,进行替换。
以上是针对项目内部改造的建议,但有的时候,大泥球项目或者shit山项目,这种升级改造成本和风险会很高,这种时候,还有另一种服务化的改造方案,把新的接入方案作为一个项目的外部服务,创建新的工程,提供服务化接口,然后通过rpc或者http的方式,和旧的项目进行交互,这样不会影响现有工程的功能稳定性,但是需要考虑服务交互治理方面的考虑
以上是 Java项目中如何同时使用两个版本的jar? 的全部内容, 来源链接: utcz.com/p/945224.html