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

Другие вопросы по тегам