如何在Java项目中同时使用两个版本的bcprov-jdk15on Jar包?
解决方案
方案一:Maven Shade插件重命名bcprov-jdk15on-1.69.jar的包名
这是低侵入且易维护的方案,通过修改新版本bcprov的包名,让它与旧版本完全隔离,从根源避免类路径冲突。
操作步骤:
- 在项目Maven pom.xml中配置Shade插件,将
bcprov-jdk15on-1.69的包名重命名(例如从org.bouncycastle改为org.bouncycastle.v169):
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <relocations> <relocation> <pattern>org.bouncycastle</pattern> <shadedPattern>org.bouncycastle.v169</shadedPattern> </relocation> </relocations> <artifactSet> <includes> <include>org.bouncycastle:bcprov-jdk15on</include> </includes> </artifactSet> </configuration> </execution> </executions> </plugin> </plugins> </build>
- 给新引入的
fintech-java-sdk-core和fintech-java-sdk-kdft依赖排除默认的bcprov依赖:
<dependencies> <!-- 旧版本bcprov,供原有功能使用 --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.54</version> </dependency> <!-- 新SDK依赖,排除自带的bcprov --> <dependency> <groupId>xxx</groupId> <artifactId>fintech-java-sdk-core</artifactId> <version>3.1.0</version> <exclusions> <exclusion> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>xxx</groupId> <artifactId>fintech-java-sdk-kdft</artifactId> <version>3.1.0</version> <exclusions> <exclusion> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> </exclusion> </exclusions> </dependency> <!-- 重命名后的新版本bcprov,供新SDK使用 --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.69</version> </dependency> </dependencies>
- 执行
mvn package后,重命名后的bcprov包会被整合到项目Jar中。新SDK会加载重命名后的类,原有功能仍加载旧版本的org.bouncycastle包,完全隔离无冲突。
方案二:自定义类加载器隔离新SDK与新版本bcprov
如果不想修改包名,可以通过自定义类加载器,让新SDK和1.69版本的bcprov在独立类加载空间运行,与系统类加载器加载的旧版本隔离。
优化实现方式:
无需逐个加载类,直接将新SDK的Jar和1.69的bcprov Jar作为自定义类加载器的类路径,通过该加载器加载新SDK入口类,后续新SDK内部类都会由这个加载器加载,自然使用1.69版本的bcprov。
示例代码:
import java.io.File; import java.net.URL; import java.net.URLClassLoader; public class SdkClassLoader extends URLClassLoader { public SdkClassLoader(String[] jarPaths) throws Exception { super(getUrls(jarPaths), Thread.currentThread().getContextClassLoader().getParent()); } private static URL[] getUrls(String[] jarPaths) throws Exception { URL[] urls = new URL[jarPaths.length]; for (int i = 0; i < jarPaths.length; i++) { urls[i] = new File(jarPaths[i]).toURI().toURL(); } return urls; } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { Class<?> c = findLoadedClass(name); if (c == null) { try { // 优先从自定义Jar路径加载 c = findClass(name); } catch (ClassNotFoundException e) { // 找不到则委托父类加载器处理JDK核心类等 c = super.loadClass(name, resolve); } } if (resolve) { resolveClass(c); } return c; } } }
使用示例:
public class SdkWrapper { public static void main(String[] args) throws Exception { String[] jarPaths = { "/path/to/bcprov-jdk15on-1.69.jar", "/path/to/fintech-java-sdk-core-3.1.0.jar", "/path/to/fintech-java-sdk-kdft-3.1.0.jar" }; SdkClassLoader classLoader = new SdkClassLoader(jarPaths); Class<?> kdConfigClass = classLoader.loadClass("com.xxx.KdSdkConfig"); Object defaultClient = kdConfigClass.getMethod("getDefaultClient").invoke(null); // 后续操作通过反射完成,可封装Wrapper类简化调用 } }
这种方式下,新SDK的所有类由自定义类加载器加载,优先使用1.69版本bcprov;原有功能由系统类加载器加载,使用1.54版本,实现完全隔离。
方案三:OSGi容器隔离(适合模块化架构项目)
如果项目本身是模块化架构,可将新SDK和1.69 bcprov打包为一个OSGi bundle,旧版本bcprov与原有功能作为另一个bundle。OSGi的模块隔离机制会保证不同bundle使用各自依赖的Jar版本,但该方案对项目架构改动较大,适合本身已使用OSGi的项目。
内容的提问来源于stack exchange,提问作者yang37
相关产品推荐
相关产品推荐

