【问题标题】:How to load 2 versions of the same Jar in 1 class Java如何在 1 类 Java 中加载同一个 Jar 的 2 个版本
【发布时间】:2017-11-01 20:46:08
【问题描述】:

这是一个复杂的问题,但我会尽力描述我的问题。

我需要在顶级类(v1.jar 和 v2.jar)中加载同一个 JAR 的 2 个版本,因此我可以访问这两个版本的 jar。这样做的原因是因为我想测试 v2.jar 中的任何功能是否从 v1.jar 回归

在我的顶级类中,我想调用 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 个不同的实现类。然后在顶层我可以使用类加载器来加载 v1 和 v2 jar 等。但是这种方式不起作用,因为我必须更改生产 v1.jar 来实现通用接口。

任何帮助或见解将不胜感激。如果可能的话,我非常想看看样品。并且请不要将我推荐给其他线程

【问题讨论】:

    标签: java maven reflection jar classloader


    【解决方案1】:

    您的测试类可以为每个 .jar 文件设置一个ClassLoader。最简单的方法是使用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 文件都不在测试代码的类路径中,因此代码不能从 .jar 文件中声明任何类型的变量。所有来自测试代码的访问都必须使用反射来完成。


    更新

    您可能还需要在调用时更改上下文类加载器:

    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
    

    【讨论】:

    • 我一直在寻找类似的东西。但是,当我尝试实现此功能时,我得到了 java.lang.ClassNotFoundException: org.example.Engine.run 异常。
    • @mosawi 好吧,你的班级实际上是这样命名的吗?
    • 不,不是,我要加载的类实际上被称为别的东西,所以我得到的例外是类的实际名称。但我想坚持你的例子,我认为它在保持简单方面做得很好。
    • 我应该提到我尝试加载的 Engine 类是 public final class Engine{public static String run(String a, String b, String c) { // some logic... }} 会改变我们从 loadClass 调用类的方式吗?
    • @mosawi 如果run() 方法是static,为什么您的示例代码会创建对象的实例?如果run() 方法需要3 个参数,为什么你的示例代码没有给出任何参数。这个答案对这个问题有效。如果您的代码不同,那么调整反射逻辑以适合您的实际代码。
    【解决方案2】:

    我从另一个 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);
           }
       }
    

    为我的目的工作

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-06-28
      • 1970-01-01
      • 2010-12-14
      • 2018-03-14
      • 2012-07-30
      • 1970-01-01
      • 2011-08-13
      相关资源
      最近更新 更多