如何在1类Java

时间:2017-05-31 21:34:48

标签: java maven reflection jar classloader

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

我需要在顶级类(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等等。但是这种方式不起作用,因为我必须更改生产v1.jar来实现公共接口。

非常感谢任何帮助或见解。如果可能的话,我非常希望看到样品。请不要将我推荐给其他主题

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:-1)

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

适用于我的目的