2014-05-16 5 views
1

사용자가 자바 코드를 텍스트 영역에 입력 한 다음 컴파일하여 "플러그인"의 일종으로 프로그램에로드 할 수있는 프로그램을 작성 중입니다. 현재 .java 파일을 컴파일하고 외부 클래스를로드 할 수 있지만 오류없이 사용자가 작성한 내부 클래스를로드/인스턴스화 할 수는 없습니다. 현재 이것은 외부 클래스를로드하는 데 사용되며이 코드는 작동하며 합병증없이 외부 클래스를 쉽게 사용할 수 있습니다.ClassLoader를 사용하여 내부 클래스로드하기

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

파일 이름의 원래 목록이 나열된 플러그인 디렉토리에있는 모든의 .class 파일이있는 GUI로부터 전송 (당신이 오타가 말해 나는 경우 나, 더 나은 가독성을 위해 약간의 편집을했다). 사용자는로드하려는 클래스를 선택하고 버튼을 클릭 한 다음 해당 파일 이름을이 메소드에 보냅니다. 이 코드에서 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를 돌려주는 경우, 그것은 (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"을 조건에 전달하고 메서드를 실행하면 이것이 내 결과입니다.

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에 따르면 각 클래스 유형은 클래스를로드 한 클래스 로더를 추적합니다. 해당 클래스 유형이 언로드 된 클래스를 참조해야하는 경우 해당 클래스 유형을 찾아로드합니다. 단일 클래스를로드 한 직후에 클래스 로더를 닫았을 때 클래스 로더가 다른 클래스 (로컬/내부 클래스 포함)를 찾거나로드 할 수 없었습니다.

관련 문제