Перезагрузите используемые классы во время выполнения Java
Я работаю над программой, которая следит за каталогом и запускает все тесты в каталоге, когда видит изменения в каталоге.
Это требует от программы динамической загрузки классов вместо получения кэшированных копий.
Я могу динамически загружать тестовые классы. Изменения в тестах обнаруживаются и используются во время выполнения. Однако это не относится к классам, проверенным тестами.
Мой код для динамической загрузки классов и возврата списка тестовых классов:
List<Class<?>> classes = new ArrayList<Class<?>>();
for (File file : classFiles) {
String fullName = file.getPath();
String name = fullName.substring(fullName.indexOf("bin")+4)
.replace('/', '.')
.replace('\\', '.');
name = name.substring(0, name.length() - 6);
tempClass = new DynamicClassLoader(Thread.currentThread().getContextClassLoader()).findClass(name) } catch (ClassNotFoundException e1) {
// TODO Decide how to handle exception
e1.printStackTrace();
}
boolean cHasTestMethods = false;
for(Method method: tempClass.getMethods()){
if(method.isAnnotationPresent(Test.class)){
cHasTestMethods = true;
break;
}
}
if (!Modifier.isAbstract(cachedClass.getModifiers()) && cHasTestMethods) {
classes.add(tempClass);
}
}
return classes;
с DynamicClassLoader в качестве Reloader, описанного здесь Как заставить Java перезагрузить класс при создании экземпляра?
Есть идеи как это исправить? Я думал, что все классы будут загружены динамически. Однако обратите внимание, что я не перезаписываю loadclass в моем DynamicClassLoader, потому что, если я делаю мои тестовые классы, дают init
РЕДАКТИРОВАТЬ: Это не работает, класс загружается, но тесты в нем не обнаружены...
List<Request> requests = new ArrayList<Request>();
for (File file : classFiles) {
String fullName = file.getPath();
String name = fullName.substring(fullName.indexOf("bin")+4)
.replace('/', '.')
.replace('\\', '.');
name = name.substring(0, name.length() - 6);
Class<?> cachedClass = null;
Class<?> dynamicClass = null;
try {
cachedClass = Class.forName(name);
URL[] urls={ cachedClass.getProtectionDomain().getCodeSource().getLocation() };
ClassLoader delegateParent = cachedClass .getClassLoader().getParent();
URLClassLoader cl = new URLClassLoader(urls, delegateParent) ;
dynamicClass = cl.loadClass(name);
System.out.println(dynamicClass);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Редактировать редактировать: я определяю методы испытаний, как это:
for(Method method: dynamicClass.getMethods()){
if(method.isAnnotationPresent(Test.class)){
requests.add(Request.method(dynamicClass, method.getName()));
}
}
1 ответ
Если вы использовали обычай ClassLoader
точно так же как в связанном ответе это не переопределяет метод protected Class<?> loadClass(String name, boolean resolve)
, Это подразумевает, что когда JVM разрешает зависимости, она все равно делегирует родительскому загрузчику классов. И, конечно же, когда это не было делегирование родителю ClassLoader
у него был риск пропустить некоторые обязательные занятия.
Самое простое решение - установить правильный загрузчик родительского класса. Вы проходите Thread.currentThread().getContextClassLoader()
что немного странно, поскольку ваше главное намерение состоит в том, чтобы делегирование не делегировало этому загрузчику, а загружало измененные классы. Вы должны подумать о том, какие загрузчики классов существуют, а какие использовать, а какие нет. Например, если класс Foo
находится в рамках вашего текущего кода, но вы хотите (повторно) загрузить его с новым ClassLoader, Foo.class.getClassLoader().getParent()
будет правильным родителем делегата для нового ClassLoader
, Обратите внимание, что это может быть null
но это не имеет значения, так как в этом случае он использовал бы загрузчик начальной загрузки, который является правильным родителем тогда.
Обратите внимание, что при настройке правильного родителя ClassLoader
в соответствии с вашими намерениями вам не нужен этот обычай ClassLoader
больше. Реализация по умолчанию (см. URLClassLoader
) уже поступает правильно. И с текущими версиями Java это Closeable
что делает его еще более подходящим для сценариев динамической загрузки.
Вот простой пример перезагрузки класса:
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
public class ReloadMyClass
{
public static void main(String[] args)
throws ClassNotFoundException, IOException {
Class<?> myClass=ReloadMyClass.class;
System.out.printf("my class is Class@%x%n", myClass.hashCode());
System.out.println("reloading");
URL[] urls={ myClass.getProtectionDomain().getCodeSource().getLocation() };
ClassLoader delegateParent = myClass.getClassLoader().getParent();
try(URLClassLoader cl=new URLClassLoader(urls, delegateParent)) {
Class<?> reloaded=cl.loadClass(myClass.getName());
System.out.printf("reloaded my class: Class@%x%n", reloaded.hashCode());
System.out.println("Different classes: "+(myClass!=reloaded));
}
}
}