java - 使用类加载器加载内部类

标签 java inner-classes urlclassloader

我正在编写一个程序,允许用户在文本区域中键入 java 代码,然后对其进行编译并将其作为一种“插件”加载到程序中。我目前能够编译 .java 文件并加载外部类,但无法正确加载/实例化用户编写的内部类。目前,这是我用来加载外部类的方法,该代码可以工作,并且我能够轻松使用外部类,而不会出现任何复杂情况。 (为了更好的可读性,我做了一些编辑,如果您发现拼写错误请告诉我)

private ArrayList<String> execute(ArrayList<String> fileNames) {
    ArrayList<String> successStories = new ArrayList();
    ArrayList<Class<?>> eventHandlers = new ArrayList();
    // Load all classes first...
    for (int i = 0; i < fileNames.size(); i++) {
        Class<?> clazz = loadClassByName2(fileNames.get(i));
        if (EventHandler.class.isAssignableFrom(clazz)) {
            eventHandlers.add(clazz);
            successStories.add(fileNames.get(i));
        } else if (InterfaceInnerClass.class.isAssignableFrom(clazz)) {
            successStories.add(fileNames.get(i));
        } else {
            System.out.println(clazz.getName() + " couldn't be loaded");
        }
    }
    // Then instantiate the handlers.
    for (int i = 0; i < eventHandlers.size(); i++) {
        try {
            Object obj = eventHandlers.get(i).newInstance();
            if (obj instanceof EventHandler) {
                EventHandler EH = (EventHandler)obj;
                EH.name = EH.getClass().getSimpleName();
                CmdEvents.addEvent(EH);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    return successStories;
}

public static Class<?> loadClassByName2(String name) {
    try {
        // My program sets up classpath environment variables so "./" is all that is needed as the URL
        URLClassLoader classLoader = new URLClassLoader(
                new URL[] { new File("./").toURI().toURL() });
        // Load the class from the classloader by name....
        Class<?> c = classLoader.loadClass("plugins.event_handlers." + name);
        classLoader.close();
        return c;
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

原始文件名列表是从 GUI 发送的,其中列出了插件目录中的每个 .class 文件。用户选择要加载的类,单击按钮,然后将这些文件名发送到这些方法。在此代码中,EventHandler 是一个类,您将在下面看到,InterfaceInnerClass 只是一个用作标记的接口(interface),以确保不存在任何严重问题,而 CmdEvents 是我的程序中用于管理这些“插件”类的控制台命令。就像我上面说的,这段代码对于外部类工作得很好,问题是当我尝试加载内部类时。我的EventHandler抽象类的代码如下。

public abstract class EventHandler {
    public String name; // Don't mind this being public, I have my reasons for this.

    public abstract void execute(String input);
    public abstract boolean condition(String input);
}

我的程序的工作方式是,它从用户接收一个字符串,然后调用条件(字符串),如果返回 true,则调用执行(字符串)。我编写了一些测试代码来尝试我的加载程序,如下所示。

package plugins.event_handlers;
public class Test_Handler extends events.EventHandler {

    public void execute(String input) {
        System.out.println("Testing...");

        TestInner inner = new TestInner();
        inner.test();

        System.out.println("Did it work?");
    }
    public boolean condition(String input) {
        return input.contains("testinput");
    }
    public class TestInner implements events.InterfaceInnerClass {
        public TestInner() {
            System.out.println("The inner works!");
        }

        public void test() {
            System.out.println("Inner class has been tested");
        }
    }
}

我运行我的程序,选择 Test_Handler.class 和 Test_Handler$TestInner.class,然后单击按钮。当该方法返回 ArrayList 或成功加载的类时,它同时返回外部类和内部类。但是,当我运行程序并将“testinput”传递给条件和执行方法时,这是我的输出。

Testing... Exception in thread "Execute_Thread_Test_Handler" java.lang.NoClassDefFoundError: plugins/event_handlers/Test_Handler$TestInner at plugins.event_handlers.Test_Handler.execute(Test_Handler.java:11) at events.ThreadEventExecutor.run(ThreadEventExecutor.java:20) Caused by: java.lang.ClassNotFoundException: plugins.event_handlers.Test_Handler$TestInner at java.net.URLClassLoader$1.run(Unknown Source) at java.net.URLClassLoader$1.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) at java.lang.ClassLoader.loadClass(Unknown Source) ... 2 more

我想要它打印的是

Testing... The inner works! Inner class has been tested Did it work?

最后我的问题是,如何使上面的代码工作?我不想让我的用户编写自己的类加载器等来加载内部/单独的类(因为并非所有我的用户都一定会在编码方面表现出色),所以我需要能够引用内部类类型而无需代码爆炸。

最佳答案

自从我问起以来已经很长时间了,但我在我的代码和类加载器 javadocs 之间来回查看,我只是很愚蠢。

在我的 loadClassByName2 中,我调用 classLoader.close();如果新加载的类要加载更多类,则不应执行此操作。

根据 javadoc,每个类类型都会跟踪加载它的类加载器。如果该类类型需要引用已卸载的类,它会调用其类加载器来查找并加载它。当我在加载单个类后立即关闭类加载器时,我做到了,因此类加载器无法找到/加载任何其他类(包括本地/内部类)。

关于java - 使用类加载器加载内部类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23693296/

相关文章:

Java 从动态外部 jar 中读取文件

Java ClassLoader - 强制重新加载已经加载的类

java - 从 Jenkins grails 插件启动的 Grails 测试不会获取 gradle build 生成的 jars

c# - 如何从浏览器应用程序控制扫描仪?

java - hybris 促销优惠券不适用于总价

java - 从内部类中获取数据

java - 将 Objective-C 结构数组转换为 Java

java - 如何在java中的另一个类中使用嵌套类?

java - 使用静态嵌套类作为 Spring bean

java - 尽管我的类(class)已加载,但 Class.forName 会抛出 ClassNotFoundException