2014-05-16 116 views
1

我正在编写一个程序,它允许用户将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只是一个用作标签的接口,以确保没有任何严重问题,并且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或成功加载的类时,它将返回BOTH外部类和内部类。但是,当我运行程序并将“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?

所以最后我的问题,我该如何让上面的代码工作?我不想让我的用户编写他们自己的类加载器,并加载一个内部/单独的类(因为不是所有用户在编码​​时都会一定很惊人),所以我需要能够引用内部类类型,而不需要代码炸毁。

回答

0

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

在我的loadClassByName2我调用classLoader.close();如果新加载的类将要加载更多类,则不应该这样做。

根据javadocs,每个类类型都跟踪加载它的类加载器。如果这个类的类型需要引用一个未加载的类,它会调用它的类加载器来查找并加载它。当我加载单个类后立即关闭类加载器时,我做了这样的事情,类加载器无法找到/加载任何其他类(包括本地/内部类)。