【问题标题】:Need to replace the existing class in runtime by a dynamically generated and compiled java .classfile at runtime需要在运行时用动态生成和编译的 java .classfile 替换运行时的现有类
【发布时间】:2014-08-06 06:15:02
【问题描述】:

我的目的是在运行时将新属性 + getter setter 方法动态注入到类定义中。目前我有一种方法可以使用新添加的属性重新生成代码,然后编译生成的代码。

最初我会在编译时为每个类创建一个模板。在运行项目时,模板类被加载到运行时。我写了一些代码来动态生成java代码并编译它。当我使用下面的代码加载新创建的类时,我无法访问注入的方法。我认为我无法覆盖现有的运行时定义。我浏览了很多博客,但仍然不明白为什么。请帮忙。

我正在访问 DROOLS 中新添加的方法,并且它没有在任何其他类中引用,这可能会在编译期间引发问题。具有新属性的规则引擎的规则在运行时更新,因此我需要相应地调整我的代码。下面是类加载器代码。此代码不会引发任何异常,但无法解决我的目的。不确定编码是否正确。

public static boolean loadClass2RunTime() {
    try {
        File folder = new File("target");
        File dir = new File(folder, "com/itap/template");
        File[] classFiles = dir.listFiles();
        URL[] url = new URL[] { folder.toURI().toURL() };

        int i = 0;
        for (File classFile : classFiles) {
            if (classFile.getName().matches(".*\\.class")) {
                System.out.println(classFile.getName().substring(0,
                        classFile.getName().lastIndexOf(".")));

                ClassLoader loader = URLClassLoader
                        .newInstance(new URL[] { folder.toURI().toURL() });

                Class cls = loader.loadClass("com.itap.template."
                        + classFile.getName().substring(0,
                                classFile.getName().lastIndexOf(".")));
                ClassLoader temp = cls.getClassLoader();
            }
        }

        return true;

    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (MalformedURLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return false;
}

【问题讨论】:

  • 这听起来像是 XY 问题。如果我了解您的要求,Groovy ExpandoMetaClass 应该可以满足您的需求。

标签: java reflection code-generation dynamic-class-loaders runtime-compilation


【解决方案1】:

URLClassLoader 在您需要在运行时加载新生成的类时很有用。不幸的是,您将无法使用 URLCLassLoader 重新加载已加载类的定义(即重新加载类)。

当我使用下面的代码加载新创建的类时,我不是 能够访问注入的方法。我认为我无法覆盖 现有的运行时定义。

URLClassLoader 将检查指定的类是否已经加载。如果找到它只是返回指向加载类的实例。因此,您将无法覆盖现有的运行时定义。

解决方案: 一种在 Java 中支持动态类重新加载的标准方法,应该读取类的字节信息(从其 .class 文件中)并编写自定义类加载器以使用此字节信息加载类。

使用自定义 ClassLoader 时要记住的要点:

  • 要重新加载一个类,ClassLoader 的现有实例(加载该类)和该类的所有已加载实例都应该被垃圾回收。
  • 自定义类加载器必须只加载特定的类(需要加载/重新加载)。加载所有其他类(内置 Java 类或其他包中的类)的请求必须委托给其父类加载器。尝试加载内置系统类可能会导致权限异常。

类加载器在加载类时遵循的步骤是:

  1. 检查类是否已经加载。
  2. 如果未加载,请请求父类加载器加载该类。
  3. 如果父类加载器无法加载类,请尝试在该类加载器中加载它。

当您实现一个能够重新加载类的类加载器时,您需要稍微偏离这个顺序。 不应将类重载请求委托给父类加载器。

这里是示例链接,可以帮助您在 Java 中开发自定义类加载器。 http://www.javablogging.com/java-classloader-2-write-your-own-classloader/
http://tutorials.jenkov.com/java-reflection/dynamic-class-loading-reloading.html

在您发表评论后编辑:

我认为语句 InputStream 流 = getClass().getClassLoader().getResourceAsStream(name);的 示例中的 loadClassData 函数没有获取最新的 类的版本。

你是对的,它并没有选择最新版本的课程,仅仅是因为它使用现有的ClassLoader 来获取流(指向你的课程的旧版本)。

你可以使用,

FileInputStream stream = new FileInputStream("Path to your class file");
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = stream.read();

while(data != -1){
    buffer.write(data);
    data = stream.read();
}

stream.close();

byte[] classData = buffer.toByteArray();

读取类的字节信息并尝试使用您的 ClassLoader 版本(自定义 ClassLoader)加载它。

【讨论】:

  • 我浏览了提供的链接,概念很清楚。我需要通过编写自己的自定义类加载器来更改类加载顺序。我使用了与javablogging.com/java-classloader-2-write-your-own-classloader 相同的代码,并根据我的要求进行了一些修改。即使那样,也不会加载该类的最新版本。据我了解,我认为语句 InputStream stream = getClass().getClassLoader().getResourceAsStream(name);示例中的 loadClassData 函数没有获取最新版本的类。如果我遗漏了什么,请告诉我。
  • @Chandu:我在您发表评论后修改了帖子。看看它。看看有没有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-02
  • 1970-01-01
  • 2018-04-16
  • 2019-08-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多