Ищете способ перезапустить класс статических инициализаторов - PullRequest
3 голосов
/ 01 марта 2012

Есть ли способ вернуть неосновной Java-класс в только что загруженное состояние? Мне нужен эквивалент выгрузки класса и его перезагрузки с нуля. В основном меня интересуют статические инициализаторы и переменные.

Контекст проблемы: я пишу робот-грейдер для студенческого кода. Одна распространенная ошибка студента, которую я видел, заключается в неправильном использовании статических переменных. Например, рассмотрим коллекцию со статическим количеством элементов, которые она содержит. Коллекция будет работать нормально при первом ее создании и использовании, но не будет работать при следующем создании экземпляра. Если я хочу, чтобы мои тесты были как можно более модульными, мне нужен способ восстановить чистое состояние после теста.

Прямо сейчас я загружаю Класс более или менее так, и набросал, как я хочу его использовать.

    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, раздел 12.7 , поддержка выгрузки классов является необязательной (но она будет делать именно то, что я хочу). Есть ли способ сделать это, не полагаясь на нестандартные функции?

Последним средством является сборка Ant, которая запускает серию тестов в отдельных программах, но я бы хотел сделать эту работу в одном процессе, если это возможно.

Ответы [ 2 ]

3 голосов
/ 01 марта 2012

Вам не обязательно выгружать класс для каждого запуска, просто используйте новый загрузчик классов. Вы можете создать новый экземпляр URLClassLoader и загрузить код своих студентов, используя его (в отличие от размещения кода ваших студентов в обычном пути к классам приложений). Загрузите целевой класс, используя этот ClassLoader, а затем найдите метод по вашему выбору, используя возвращенный объект Class, и вызовите его. Если метод, который вы хотите вызвать, нестатичен, вам сначала нужно сделать несколько дополнительных шагов, чтобы создать экземпляр объекта с помощью отражения.

Пример использования типичного public static void main(String[]) метода для ввода.

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

В этом примере com.example.MyClass не должно быть в обычном пути к классу приложения. Если это так, то именно эта версия будет использоваться вместо версии, найденной пользовательским загрузчиком классов в качестве стандартных загрузчиков классов, для делегирования классов используется родительское делегирование.

1 голос
/ 01 марта 2012

Честно говоря, я бы начал новую JVM для оценки кода каждого студента.Существует слишком много возможностей для кода одного студента вмешиваться в код другого, если вы попытаетесь сделать все это в одном экземпляре JVM.Что-то простое, как бесконечный цикл ...

В качестве побочного эффекта это в значительной степени устраняет необходимость в пользовательских загрузчиках классов и так далее.

...