如何在 1 个 class Java 中加载同一个 Jar 的 2 个版本
How to load 2 versions of the same Jar in 1 class Java
这是一个复杂的问题,但我会尽力描述我的问题。
我需要在顶级 class(v1.jar 和 v2.jar)中加载同一个 JAR 的两个版本,因此我可以访问两个版本的 jar。这样做的原因是因为我想测试 v2.jar 中的任何功能是否从 v1.jar
中回归
在我的顶级 class 中,我想调用 v1.jar 和 v2.jar 的方法,然后根据 v2 输出验证 v1 的输出。这样我就可以确定没有任何事情搞砸了。
class Common {
// Names of the classes would be the same so not sure how I would invoke the classes from the 2 different jars?
String resultv1 = EngineV1.run("a","b","c");
String resultv2 = EngineV2.run("a","b","c");
Assert.equals(resultv1, resultv2, "Regression has been introduced...");
}
我无法将 v1 和 v2 jar 作为 maven 依赖项导入,因为这会在 maven 中造成版本冲突,默认情况下 maven 将使用最新的 jar。所以我想过创建一个通用接口并对该接口有 2 个不同的实现 classes。然后在顶层,我可以使用 class 加载程序来加载 v1 和 v2 jar 等。但是这种方式行不通,因为我必须更改生产 v1.jar 来实现通用接口。
任何帮助或见解将不胜感激。如果可能的话,我非常想看看样品。并且请不要将我推荐给其他线程
你的测试class可以设置一个ClassLoader
for each .jar file. The easiest way to do that, is to use URLClassLoader
.
示例:
File jar1 = new File("/path/to/v1.jar");
File jar2 = new File("/path/to/v2.jar");
URLClassLoader v1Loader = URLClassLoader.newInstance(new URL[] { jar1.toURI().toURL() });
URLClassLoader v2Loader = URLClassLoader.newInstance(new URL[] { jar2.toURI().toURL() });
Class<?> engineClass1 = v1Loader.loadClass("org.example.Engine");
Class<?> engineClass2 = v2Loader.loadClass("org.example.Engine");
Method runMethod1 = engineClass1.getMethod("run");
Method runMethod2 = engineClass2.getMethod("run");
Object engine1 = engineClass1.newInstance();
Object engine2 = engineClass2.newInstance();
String result1 = (String) runMethod1.invoke(engine1);
String result2 = (String) runMethod2.invoke(engine2);
请注意,由于两个 .jar 文件都不在测试代码的 class 路径上,因此代码无法声明任何 .jar 文件类型的变量。来自测试代码的所有访问都必须使用反射来完成。
更新
您可能还需要在调用时更改上下文 class 加载程序:
String result1, result2;
Thread thread = Thread.currentThread();
ClassLoader myLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(v1Loader);
result1 = (String) runMethod1.invoke(engine1);
thread.setContextClassLoader(v2Loader);
result2 = (String) runMethod2.invoke(engine2);
} finally {
thread.setContextClassLoader(myLoader);
}
// Compare result1 and result2
我从一个不同的 Whosebug 问题中发现了这个问题,我需要在运行时加载一个 jar
/*
* Adds the supplied Java Archive library to java.class.path. This is benign
* if the library is already loaded.
*/
public static synchronized void loadLibrary(java.io.File jar) throws Exception {
try {
/*We are using reflection here to circumvent encapsulation; addURL is not public*/
java.net.URLClassLoader loader = (java.net.URLClassLoader)ClassLoader.getSystemClassLoader();
java.net.URL url = jar.toURI().toURL();
/*Disallow if already loaded*/
for (java.net.URL it : java.util.Arrays.asList(loader.getURLs())){
if (it.equals(url)){
return;
}
}
java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
method.setAccessible(true); /*promote the method to public access*/
method.invoke(loader, new Object[]{url});
} catch (final java.lang.NoSuchMethodException |
java.lang.IllegalAccessException |
java.net.MalformedURLException |
java.lang.reflect.InvocationTargetException e){
throw new Exception(e);
}
}
适合我的目的
这是一个复杂的问题,但我会尽力描述我的问题。
我需要在顶级 class(v1.jar 和 v2.jar)中加载同一个 JAR 的两个版本,因此我可以访问两个版本的 jar。这样做的原因是因为我想测试 v2.jar 中的任何功能是否从 v1.jar
中回归在我的顶级 class 中,我想调用 v1.jar 和 v2.jar 的方法,然后根据 v2 输出验证 v1 的输出。这样我就可以确定没有任何事情搞砸了。
class Common {
// Names of the classes would be the same so not sure how I would invoke the classes from the 2 different jars?
String resultv1 = EngineV1.run("a","b","c");
String resultv2 = EngineV2.run("a","b","c");
Assert.equals(resultv1, resultv2, "Regression has been introduced...");
}
我无法将 v1 和 v2 jar 作为 maven 依赖项导入,因为这会在 maven 中造成版本冲突,默认情况下 maven 将使用最新的 jar。所以我想过创建一个通用接口并对该接口有 2 个不同的实现 classes。然后在顶层,我可以使用 class 加载程序来加载 v1 和 v2 jar 等。但是这种方式行不通,因为我必须更改生产 v1.jar 来实现通用接口。
任何帮助或见解将不胜感激。如果可能的话,我非常想看看样品。并且请不要将我推荐给其他线程
你的测试class可以设置一个ClassLoader
for each .jar file. The easiest way to do that, is to use URLClassLoader
.
示例:
File jar1 = new File("/path/to/v1.jar");
File jar2 = new File("/path/to/v2.jar");
URLClassLoader v1Loader = URLClassLoader.newInstance(new URL[] { jar1.toURI().toURL() });
URLClassLoader v2Loader = URLClassLoader.newInstance(new URL[] { jar2.toURI().toURL() });
Class<?> engineClass1 = v1Loader.loadClass("org.example.Engine");
Class<?> engineClass2 = v2Loader.loadClass("org.example.Engine");
Method runMethod1 = engineClass1.getMethod("run");
Method runMethod2 = engineClass2.getMethod("run");
Object engine1 = engineClass1.newInstance();
Object engine2 = engineClass2.newInstance();
String result1 = (String) runMethod1.invoke(engine1);
String result2 = (String) runMethod2.invoke(engine2);
请注意,由于两个 .jar 文件都不在测试代码的 class 路径上,因此代码无法声明任何 .jar 文件类型的变量。来自测试代码的所有访问都必须使用反射来完成。
更新
您可能还需要在调用时更改上下文 class 加载程序:
String result1, result2;
Thread thread = Thread.currentThread();
ClassLoader myLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(v1Loader);
result1 = (String) runMethod1.invoke(engine1);
thread.setContextClassLoader(v2Loader);
result2 = (String) runMethod2.invoke(engine2);
} finally {
thread.setContextClassLoader(myLoader);
}
// Compare result1 and result2
我从一个不同的 Whosebug 问题中发现了这个问题,我需要在运行时加载一个 jar
/*
* Adds the supplied Java Archive library to java.class.path. This is benign
* if the library is already loaded.
*/
public static synchronized void loadLibrary(java.io.File jar) throws Exception {
try {
/*We are using reflection here to circumvent encapsulation; addURL is not public*/
java.net.URLClassLoader loader = (java.net.URLClassLoader)ClassLoader.getSystemClassLoader();
java.net.URL url = jar.toURI().toURL();
/*Disallow if already loaded*/
for (java.net.URL it : java.util.Arrays.asList(loader.getURLs())){
if (it.equals(url)){
return;
}
}
java.lang.reflect.Method method = java.net.URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{java.net.URL.class});
method.setAccessible(true); /*promote the method to public access*/
method.invoke(loader, new Object[]{url});
} catch (final java.lang.NoSuchMethodException |
java.lang.IllegalAccessException |
java.net.MalformedURLException |
java.lang.reflect.InvocationTargetException e){
throw new Exception(e);
}
}
适合我的目的