Перезагрузить файл класса в Tomcat
Я создаю файл класса во время выполнения.
Я хочу заменить существующий файл класса на обновленный в загрузчике классов.
Это похоже на горячую замену (например, JRebel), которая позволяет избежать перезапуска и повторного развертывания сервера.
Я нашел context.xml подход для tomcat для перезагрузки контекста. Но это не очень полезно в случае производственной среды.
Можем ли мы зарегистрировать класс в ClassLoader во время выполнения? Пожалуйста, предложите, если есть какая-либо альтернатива для перезагрузки класса во время выполнения.
Я использую следующий код для получения текущего classLoader.
ClassLoader classLoader = LoggingAspect.class.getClassLoader();
Ниже приведена реализация метода класса нагрузки.
public class AspectClassLoader extends ClassLoader{
@Override
public synchronized Class<?> loadClass(String name) throws ClassNotFoundException
{
String customLoadClass = "com.log.LoggingAspect";
try
{
if(!customLoadClass.equals(name))
{
return super.loadClass(name);
}
else
{
URL classLoadUrl = new URL(this.reloadClassUrl);
URLConnection connection = classLoadUrl.openConnection();
InputStream input = connection.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = input.read();
while(data != -1){
buffer.write(data);
data = input.read();
}
input.close();
byte[] classData = buffer.toByteArray();
return defineClass(name, classData, 0, classData.length);
}
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
Для перезагрузки класса я использую следующий код.
AspectClassLoader urlClsLoader = new AspectClassLoader(classLoader);
Class aspect = urlClsLoader.reloadClass("com.log.LoggingAspect", true, new String("file:"+className));
Мой метод перезагрузки загрузчика классов
public synchronized Class<?> reloadClass(String name, boolean resolve, String reloadClassUrl) throws ClassNotFoundException
{
this.reloadClassUrl = reloadClassUrl;
return this.loadClass(name, resolve);
}
После перезагрузки, если я создаю новый экземпляр LoggingAspect, он все равно возвращает мне старый экземпляр. Пожалуйста, предложите.
Class aspect = urlClsLoader.reloadClass("com.log.LoggingAspect", true, new String("file:"+className));
com.log.aspect.Aspect aspectObj = (com.log.aspect.Aspect)aspect.newInstance();
Тем не менее я получаю старый экземпляр.
Подскажите, пожалуйста, почему classloader не загружает модифицированный класс?
1 ответ
Это возможно, но сложно. Что вам нужно сделать, это создать новый URLClassLoader
с текущим ClassLoader
как родитель. Добавьте URL к папке с вашим классом (без папок пакета) в новый URLClassLoader
Далее вам придется патчить loadClass()
вернуть свой новый класс перед вызовом parent.loadClass()
(в противном случае будет использован существующий класс, а обновленный будет игнорироваться).
Это становится сложным, когда вы хотите использовать новый класс. Для этого вам нужно будет создать его с помощью нового загрузчика классов и убедиться, что все используют этот экземпляр типа.
Вероятно, это более безопасно (и легче отлаживать), если класс не существует в вашей WAR. Вместо использования типа загрузите класс с URLClassLoader
и создать экземпляр этого. Таким образом, код, использующий этот класс, работает так:
IService service = createService();
где IService
интерфейс, который определяет методы, которые поддерживает сгенерированный класс Таким образом, вы можете написать код, который использует методы без фактической реализации.