【问题标题】:Reload used classes at runtime Java在运行时重新加载使用的类 Java
【发布时间】:2013-12-04 03:48:10
【问题描述】:

我正在开发一个程序,它监视目录并在看到目录中的更改时运行目录中的所有测试。

这需要程序动态加载类,而不是获取缓存的副本。

我可以动态加载测试类。对测试的更改在运行时被检测和使用。但是,测试所测试的类并非如此。

我用于动态加载类并返回测试类列表的代码:

List<Class<?>> classes = new ArrayList<Class<?>>();
    for (File file : classFiles) {
        String fullName = file.getPath();
        String name = fullName.substring(fullName.indexOf("bin")+4)
                .replace('/', '.')
                .replace('\\', '.'); 
        name = name.substring(0, name.length() - 6);

            tempClass = new DynamicClassLoader(Thread.currentThread().getContextClassLoader()).findClass(name)          } catch (ClassNotFoundException e1) {
            // TODO Decide how to handle exception
            e1.printStackTrace();
        }

        boolean cHasTestMethods = false;
        for(Method method: tempClass.getMethods()){
            if(method.isAnnotationPresent(Test.class)){
                cHasTestMethods = true;
                break;
            }
        }
        if (!Modifier.isAbstract(cachedClass.getModifiers()) && cHasTestMethods) {
            classes.add(tempClass);
        }
    }
    return classes;

DynamicClassLoader 作为此处描述的 Reloader How to force Java to reload class upon instantiation?

知道如何解决吗?我认为所有类都会被动态加载。但是请注意,我不会在我的 DynamicClassLoader 中覆盖 loadclass,因为如果我这样做,我的测试类会给出 init

编辑: 这不起作用,该类已加载但未检测到其中的测试...

List<Request> requests = new ArrayList<Request>();
    for (File file : classFiles) {
        String fullName = file.getPath();
        String name = fullName.substring(fullName.indexOf("bin")+4)
                .replace('/', '.')
                .replace('\\', '.'); 
        name = name.substring(0, name.length() - 6);
        Class<?> cachedClass = null;
        Class<?> dynamicClass = null;
        try {
            cachedClass = Class.forName(name);


            URL[] urls={ cachedClass.getProtectionDomain().getCodeSource().getLocation() };
            ClassLoader delegateParent = cachedClass .getClassLoader().getParent();
            URLClassLoader cl = new URLClassLoader(urls, delegateParent) ;
            dynamicClass = cl.loadClass(name);
            System.out.println(dynamicClass);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

编辑编辑:我检测这样的测试方法:

            for(Method method: dynamicClass.getMethods()){
            if(method.isAnnotationPresent(Test.class)){
                requests.add(Request.method(dynamicClass, method.getName()));
            }
        }

【问题讨论】:

  • 呃,你的代码只有一个字符吗?

标签: java reloading


【解决方案1】:

如果您使用自定义 ClassLoader 与链接答案中的完全一样,则它不会覆盖方法 protected Class&lt;?&gt; loadClass(String name, boolean resolve)。这意味着当 JVM 解决依赖关系时,它仍然会委托给父类加载器。当然,当它没有委托给父 ClassLoader 时,它有可能错过一些必需的课程。

最简单的解决方案是设置正确的父类加载器。您当前正在传递Thread.currentThread().getContextClassLoader(),这有点奇怪,因为您的主要意图是委托应该委托给该加载器,而是加载更改的类。您必须考虑存在哪些类加载器,使用哪些类加载器,哪些不使用。例如。如果 Foo 类在您当前代码的范围内,但您想使用新的 ClassLoader(重新)加载它,Foo.class.getClassLoader().getParent() 将是新 ClassLoader 的正确委托父级。请注意,它可能是null,但这并不重要,因为在这种情况下,它将使用引导加载程序,它是正确的父级。

请注意,当您设置正确的父 ClassLoader 匹配您的意图时,您不再需要该自定义 ClassLoader。默认实现(参见URLClassLoader)已经做了正确的事情。在当前的 Java 版本中,Closeable 使其更适合动态加载场景。

这是一个类重载的简单示例:

import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;

public class ReloadMyClass
{
  public static void main(String[] args)
  throws ClassNotFoundException, IOException {
    Class<?> myClass=ReloadMyClass.class;
    System.out.printf("my class is Class@%x%n", myClass.hashCode());
    System.out.println("reloading");
    URL[] urls={ myClass.getProtectionDomain().getCodeSource().getLocation() };
    ClassLoader delegateParent = myClass.getClassLoader().getParent();
    try(URLClassLoader cl=new URLClassLoader(urls, delegateParent)) {
      Class<?> reloaded=cl.loadClass(myClass.getName());
      System.out.printf("reloaded my class: Class@%x%n", reloaded.hashCode());
      System.out.println("Different classes: "+(myClass!=reloaded));
    }
  }
}

【讨论】:

  • 好的,我想我明白你的回答了。我已经尝试过了,这确实动态加载了一个类。但是,我仍然不确定如何确保重新加载所有类。基本上,我有一个使用类(它测试的类)的测试类,它需要在运行时使用所有类的最新版本。因此,如果在运行时我在类中运行测试,它会在运行时使用最新版本。这可以动态重新加载测试类,但它不会为该类中使用的类执行此操作。
  • 您必须确保将测试用例类和被测类的代码源(即 URL)提供给新的类加载器,而不是提供给其父加载器链之一。所有引用的类都通过新的类加载器解析,因此如果类加载器具有正确的设置,则会重新加载。
  • 我试图让你的例子适应我的例子,但是在运行时,它会加载类,但是当我尝试使用 dynamicClass 进行测试时,它给了我一个初始化错误,我认为它不是t 正确加载。
  • 我的示例使用带有资源结构的try,这将在最后关闭ClassLoader。关闭后,无法加载任何类,因此解析尚未初始化的类的依赖关系将失败。因此,动态加载类的所有使用都必须在该块内进行。如果这不可行,请不要使用此 try 构造。
  • 在上面编辑中发布的示例代码中,我没有那个 try 块。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-19
  • 1970-01-01
  • 2012-04-11
  • 1970-01-01
相关资源
最近更新 更多