简体   繁体   English

使用ClassLoader加载内部类

[英]Loading Inner Classes using a ClassLoader

I am writing a program that lets users type java code into a text area, then compile it and load it into the program as a sort of "plugin." 我正在编写一个程序,允许用户在文本区域中键入Java代码,然后对其进行编译并将其作为一种“插件”加载到程序中。 I am currently able to compile the .java files and load the outer classes, but I am not able to load/instantiate inner classes written by users without errors. 我目前能够编译.java文件并加载外部类,但是我无法正确地加载/实例化由用户编写的内部类。 Currently this is what I use to load the outer classes, this code works and I am able to easily use the outer classes without complications whatsoever. 目前,这是我用来加载外部类的方法,此代码有效,并且我能够轻松使用外部类而不会产生任何复杂性。 (I did some editing for better readability, if you notice a typo tell me) (如果您发现拼写错误,我做了一些编辑以提高可读性)

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;
    }
}

The original list of file names is sent from a GUI with every .class file in the plugin directory listed. 文件名的原始列表是从GUI发送的,列出了插件目录中的每个.class文件。 They user selects the classes they want to load, clicks a button and it sends those filenames to these methods. 用户选择要加载的类,单击按钮,然后将这些文件名发送到这些方法。 Within this code EventHandler is a class which you will see below, InterfaceInnerClass is simply an interface used as a tag to ensure there aren't any serious problems, and CmdEvents is a console command within my program used to manage these "plugin" classes. 在此代码中,EventHandler是一个类,您将在下面看到,InterfaceInnerClass只是用作标记的接口,以确保没有任何严重的问题,而CmdEvents是我程序中用于管理这些“插件”类的控制台命令。 Like I said above, this code works fine for outer classes, the problem is when I try to load inner classes. 就像我在上面说过的那样,此代码对于外部类可以正常工作,问题是当我尝试加载内部类时。 The code for my EventHandler abstract class is as follows. 我的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);
}

The way my program works, it recieves a String from a user, then calls condition(String) and if it returns true, it calls execute(String). 我的程序的工作方式是,它从用户那里接收字符串,然后调用condition(String),如果返回true,则调用execute(String)。 I wrote some test code to try out my loader as follows. 我编写了一些测试代码来尝试如下所述的加载程序。

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");
        }
    }
}

I run my program, select both the Test_Handler.class and Test_Handler$TestInner.class, then click the button. 我运行程序,选择Test_Handler.class和Test_Handler $ TestInner.class,然后单击按钮。 When the method returns an ArrayList or successfully loaded classes, it returns BOTH the outer and inner class. 当该方法返回ArrayList或成功加载的类时,它将同时返回外部类和内部类。 However, when I run the program and pass "testinput" to the condition and execute methods, this is my output. 但是,当我运行程序并将“ 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 正在测试...线程“ Execute_Thread_Test_Handler”中的异常java.lang.NoClassDefFoundError:plugins.event_handlers.Test_Handler.execute(Test_Handler.java:11)​​处的plugins / event_handlers / Test_Handler $ TestInner在events.ThreadEventExecutor.run(ThreadEventExecutor.java:20:20) )原因:java.lang.ClassNotFoundException:java.net.URLClassLoader $ 1.run(unknown Source)处的plugins.event_handlers.Test_Handler $ TestInner在java.security.AccessController处java.net.URLClassLoader $ 1.run(Unknown Source)处。 java.net.URLClassLoader.findClass(未知源)的doPrivileged(本地方法)java.lang.ClassLoader.loadClass(未知源)的java.lang.ClassLoader.loadClass(未知源)... 2另外

What I am wanting it to print is 我要打印的是

Testing... The inner works! 测试中...内部工作! Inner class has been tested Did it work? 内部班级已经过测试是否有效?

So finally my question, how do I make the above code work? 所以最后我的问题是,如何使以上代码起作用? I do not want to have to make my users write their own classloaders and such to load an inner/seperate class (because not all my users will necessarily be amazing at coding) so I need to be able to reference the inner class type without the code blowing up. 我不想让我的用户编写自己的类加载器,而不必加载一个内部/单独的类(因为并非我的所有用户都一定会对编码感到惊奇),所以我需要能够引用内部类类型而无需代码炸毁。

Well its been a very long time since I asked but I was looking back and forth between my code and the classloader javadocs and I'm simply being dumb. 自从我问了这已经很长时间了,但是我在代码和类加载器javadocs之间来回浏览,我简直是愚蠢。

In my loadClassByName2 I call classLoader.close(); 在我的loadClassByName2中,我调用classLoader.close();。 which should NOT be done if the freshly loaded class is going to load any more classes. 如果新加载的类要加载更多的类,则不应这样做。

According to the javadocs, each class type keeps track of the classloader that loaded it. 根据javadocs,每种类类型都会跟踪加载它的类加载器。 If that class type ever needs to reference an unloaded class, it calls upon its classloader to find and load it. 如果该类类型曾经需要引用一个已卸载的类,它将调用其类加载器来查找并加载它。 When I closed the classloader immediately after loading the single class I made it so the classloader was not able to find/load any other classes (including local/internal classes). 当我在加载单个类后立即关闭类加载器时,我完成了该操作,因此该类加载器无法查找/加载任何其他类(包括本地/内部类)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM