[英]Loading Inner Classes using a ClassLoader
我正在編寫一個程序,允許用戶在文本區域中鍵入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);
}
我的程序的工作方式是,它從用戶那里接收字符串,然后調用condition(String),如果返回true,則調用execute(String)。 我編寫了一些測試代碼來嘗試如下所述的加載程序。
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”傳遞給條件並執行方法時,這就是我的輸出。
正在測試...線程“ 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另外
我要打印的是
測試中...內部工作! 內部班級已經過測試是否有效?
所以最后我的問題是,如何使以上代碼起作用? 我不想讓我的用戶編寫自己的類加載器,而不必加載一個內部/單獨的類(因為並非我的所有用戶都一定會對編碼感到驚奇),所以我需要能夠引用內部類類型而無需代碼炸毀。
自從我問了這已經很長時間了,但是我在代碼和類加載器javadocs之間來回瀏覽,我簡直是愚蠢。
在我的loadClassByName2中,我調用classLoader.close();。 如果新加載的類要加載更多的類,則不應這樣做。
根據javadocs,每種類類型都會跟蹤加載它的類加載器。 如果該類類型曾經需要引用一個已卸載的類,它將調用其類加載器來查找並加載它。 當我在加載單個類后立即關閉類加載器時,我完成了該操作,因此該類加載器無法查找/加載任何其他類(包括本地/內部類)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.