简体   繁体   English

如何在1类Java中加载同一Jar的2个版本

[英]How to load 2 versions of the same Jar in 1 class Java

This is a complicated question but I will do my best to describe my problem. 这是一个复杂的问题,但我会尽力描述我的问题。

I need to load 2 versions of the same JAR in a top level class (v1.jar and v2.jar) so I have access to both versions of the jar. 我需要在顶级类(v1.jar和v2.jar)中加载同一JAR的2个版本,这样我就可以访问这两个版本的jar。 The reason for this is because I want to test if any feature in v2.jar has regressed from v1.jar 这是因为我想测试v2.jar中的任何功能是否已从v1.jar回归

In my top level class, I want to call methods of v1.jar and v2.jar and then validate the output from v1 against v2 output. 在我的顶级类中,我想调用v1.jar和v2.jar的方法,然后验证v1对v2输出的输出。 This way I can be certain nothing got screwed up. 这样我就可以确定没有搞砸了。

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...");


}

I can't import v1 and v2 jars as maven dependencies since this will create a version conflict in maven and by default maven will use the newest jar. 我无法将v1和v2 jar作为maven依赖项导入,因为这会在maven中创建版本冲突,默认情况下maven将使用最新的jar。 So I thought about creating a common interface and having 2 different implementation classes of that interface. 所以我考虑创建一个通用接口,并具有该接口的2个不同的实现类。 Then in the toplevel I can use class loaders to load v1 and v2 jars, etc. But this way not work since I would have to change production v1.jar to implement the common interface. 然后在顶层我可以使用类加载器来加载v1和v2等等。但是这种方式不起作用,因为我必须更改生产v1.jar来实现公共接口。

Any help or insight will be much appreciated. 任何帮助或见解将不胜感激。 I'd very much like to see samples if possible. 如果可能的话,我非常希望看到样品。 And please don't refer me to other threads 请不要将我介绍给其他主题

Your test class can set up a ClassLoader for each .jar file. 您的测试类可以为每个.jar文件设置ClassLoader The easiest way to do that, is to use URLClassLoader . 最简单的方法是使用URLClassLoader

Example: 例:

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);

Note that since neither .jar file is on the classpath of the test code, the code cannot declare any variables of types from the .jar files. 请注意,由于.jar文件都不在测试代码的类路径中,因此代码无法从.jar文件声明任何类型的变量。 All access from test code must be done using reflection. 必须使用反射完成测试代码的所有访问。


UPDATE UPDATE

You might also need to change the context class loader when making the calls: 您可能还需要在进行调用时更改上下文类加载器:

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

I found this from a different Stackoverflow question where I needed to load a jar during runtime 我从一个不同的Stackoverflow问题中发现了这个问题,我需要在运行时加载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);
       }
   }

Works for my purposes 适合我的目的

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM