2012-03-01 4 views
3

비 핵심 Java 클래스를 새로로드 된 상태로 되돌릴 수있는 방법이 있습니까? 클래스를 언로드하고 처음부터 다시로드하는 것과 동일한 작업을 원합니다. 나는 주로 static initializers와 변수에 관심이있다.클래스 정적 초기화 프로그램을 다시 실행하는 방법을 찾고

문제 상황 : 나는 학생 코드에 대해 로보 그레이더를 작성하고 있습니다. 제가 보았던 한 가지 일반적인 학생 오류는 정적 변수를 부적절하게 사용하는 것입니다. 예를 들어 정적 요소가 포함 된 Collection을 생각해보십시오. 컬렉션이 처음 만들어지고 사용될 때 제대로 작동하지만 다음 인스턴스에 실패합니다. 가능한 한 모듈화 된 테스트를 원한다면 테스트 후 깨끗한 상태를 복원 할 방법이 필요합니다.

지금 당장 클래스를 다소 로딩하고 있는데 어떻게 사용하고 싶은지를 그려 보았습니다.

String classUnderTest = "package.class"; 
    Class unitUnderTest; 
    try { 
     unitUnderTest = Class.forName(classUnderTest); 
    } catch (ClassNotFoundException e) { 
     System.out.println("Class \"" + classUnderTest + "\" was not found, can't continue."); 
     printGradeAndExit(); 
    } 
    // Run foundation tests (stuff on which later tests depend) using unitUnderTest.newInstance() 
    runFoundationTests(unitUnderTest); 
    // Now reset unitUnderTest for a static variable detection test 
    lookForCommonStaticVariableMistakes(unitUnderTest); 

명백하게 로보 그레이더는 완벽 할 수는 없지만 일반적인 오류를 잡아 내고 싶습니다.

Java Language Specification, Section 12.7에 따르면 수업을 지원하는 것은 선택 사항이지만 원하는대로 할 수 있습니다. 비표준 기능에 의존하지 않고이를 수행 할 수있는 방법이 있습니까?

마지막 수단은 별도의 프로그램에서 일련의 테스트를 실행하는 Ant 빌드를 수행하는 것이지만 가능한 한 단일 프로세스로이 작업을하고 싶습니다.

+0

정적 초기화 프로그램을 사용하여 게시 한 코드에는 아무 것도 보이지 않지만 훨씬 덜 '다시 실행'합니다. – Perception

+0

unitUnderTest가로드되고 newInstance()가 호출되면 클래스의 정적 초기화자가 실행됩니다. – Mike

답변

3

새 클래스 로더를 사용하기 만하면 각 실행마다 반드시 클래스를 언로드 할 필요는 없습니다. URLClassLoader의 새 인스턴스를 만들고이를 사용하여 학생 코드를로드 할 수 있습니다 (학생 코드를 일반 응용 프로그램 클래스 경로에 배치하는 것과 반대). 해당 ClassLoader를 사용하여 대상 클래스를로드 한 다음 반환 된 Class 객체를 사용하여 원하는 메서드를 찾고 호출합니다. 호출 할 메소드가 정적이 아닌 경우 먼저 리플렉션을 통해 객체의 인스턴스를 만드는 데 몇 가지 추가 단계를 수행해야합니다.

예제는 일반적으로 public static void main(String[]) 입력 방법을 사용합니다. 이 본 예에서 com.example.MyClass

String[] args = new String[0]; //Add arguments for main call. 

//Add whatever classpath elements you need. 
String[] classpath = {"file:///example/path/to/studentProjectJarFile.jar", 
         "file:///example/path/to/studentProjectDir/"}; 

//Create classloader. 
ClassLoader cl = 
      new URLClassLoader(classpath); 

Class<?> mainClazz; 
Method mainMethod; 

//Find Class. 
mainClazz = cl.loadClass("com.example.MyClass"); 
//Find the target method. 
mainMethod = mainClazz.getMethod("main", String[].class); 
//Invoke method. 
mainMethod.invoke(null, (Object) args); 

정규 애플리케이션 클래스 패스이어야한다. 그렇다면 표준 클래스 로더는 클래스를로드하기 위해 상위 우선 위임을 사용하므로 사용자 정의 클래스 로더 대신 사용되는 버전이됩니다.

+0

나는 너가 어디로 가는지 보았다고 생각한다. 알아 내려면 조심스럽게 API를 읽어야 할 것입니다.하지만 일반적인 생각이납니다. 새 ClassLoader (URL 또는 기타)를 사용하여 새 인스턴스를 만들 수 있어야하며 각 인스턴스는 별도의 정적 초기화입니다. 그건 내 구체적인 필요에 맞게 여기에. 그것은 또한 종속 클래스의 재로드로 이어질 것입니까? 예 : 정적 내부 클래스? 나는 지금 그것을 필요로하지 않는다. 그러나 나는 그것을 필요로하는 것을 앞으로 볼 수 있었다. – Mike

+0

@ 마이크 예를 들어 답을 업데이트했습니다.귀하의 질문에 대답 : 예, 클래스가 시스템 클래스 로더 (응용 프로그램 클래스 경로), 확장 클래스 로더 (jre 확장 클래스) 또는 부트 스트랩 클래스 로더 (jre)와 같이 사용자 정의 클래스 로더의 상위 클래스 로더에 의해로드되지 않는 한 클래스). – Dev

1

솔직히 말해서, 나는 각 학생의 코드를 평가하기 위해 새로운 JVM을 시작하려고합니다. 한 학생의 코드가 하나의 JVM 인스턴스에서 모두 수행하려고 시도하는 경우 다른 학생의 코드를 간섭 할 기회가 너무 많습니다. 무한 루프만큼이나 단순한 것 ...

부작용으로 이것은 맞춤 클래스 로더 등의 필요성을 크게 제거합니다.

+0

그것은 이미 계획입니다. 각 학생은 새로운 JVM을 얻습니다. 그러나 하나의 학생 프로젝트에서 여러 모듈 테스트를 위해 새로운 JVM을 사용 하시겠습니까? 그것은 약간입니다. – Mike

+1

JVM 시작이 100ms 범위에 있으며 수백 개의 테스트를 실행하는 데 매우 느리다는 점에 유의하고 싶습니다. – Nayuki

+0

@Mike - "조금"- 시간이나 컴퓨터의 시간을 더 중요시하는지 여부에 달려 있습니다. 매우 느린 것은 상대적입니다. (FWIW, 나는 150 여명의 학생들을 대상으로 이런 종류의 일을 해왔다. 필자는 학생들이 미리 보지 못했던 JUnit 테스트로서 자동 마킹을 구현했다.) –