Разрешение класса родительско-дочернего загрузчика классов

Задачи и первоначальное расследование

Я пытаюсь настроить два экземпляра Oracle Coherence рядом с кешем в одном приложении Java Swing. Идею решения можно найти здесь. Мой случай немного сложнее, и здесь начинается игра.

Краткое описание

В моем случае есть служба учетных записей. Он может иметь две конечные точки: SIT и UAT. Чтобы создать два таких сервиса, мне нужно загрузить два «экземпляра» Coherence, чтобы переопределить конечные точки системными переменными (tangosol.coherence.cacheconfig).

У меня есть:

  • основной код приложения находится в файле mainapp.jar;
  • интерфейс AccountService, расположенный в файле account-interfaces.jar;
  • класс AccountServiceImpl, расположенный в файле account-impl.jar и реализующий интерфейс AccountService;
  • мое основное приложение имеет следующую структуру
    bin: startup.bat, startup.sh
    conf: app.properties
    lib: mainapp.jar, account-interfaces.jar, account-impl.jar, coherence.jar
    

Подход попробовал

Я создал выделенный дочерний classLoader - InverseClassLoader и сделал AppLaunchClassLoader (по умолчанию Thread.currentThread().GetContextClassLoader() classLoader) его родителем. С помощью InverseClassLoader я загружаю класс AccountServiceImpl:

Class<AccountServiceImpl> acImplClass = contextClassLoader.selfLoad(AccountServiceImpl.class).loadClass(AccountServiceImpl.class);
Constructor<AccountServiceImpl> acConstructor =
acImplClass .getConstructor(String.class); 
AccountService acService = acConstructor .newInstance(serviceURL);

Проблемы и вопросы

  1. Я получаю, что «AccountServiceImpl не может быть приведен к исключениям AccountService», что означает, что эти два класса загружаются разными загрузчиками классов. Но эти загрузчики классов находятся в отношениях родитель-потомок. Итак, я прав, что даже если класс загружается родителем (интерфейс - «абстрактный» тип), его нельзя использовать с классом (конкретным внедрением), загруженным дочерним загрузчиком классов? Зачем тогда нам нужны эти отношения родитель-потомок?
  2. Я указал интерфейс AccountService в коде, и он был загружен загрузчиком классов по умолчанию. Я попытался обернуть приведенный выше код потоком и установить InverseClassLoader в контекстный загрузчик классов. Ничего не изменилось. Итак, я прав, что я не могу использовать такое кодирование реализации интерфейса (как обычное кодирование) и мне нужно все время использовать отражение для постоянного вызова конкретных методов? (Надеюсь есть решение) ;
  3. Скажем, я перечислил классы AccountService и AccountServiceImpl для загрузки с помощью InverseClassLoader. Что, если мне нужно, чтобы другие классы, доступные этим двум, также загружались с помощью InverseClassLoader? Есть ли способ сказать, что все «связанные» классы должны быть загружены одним и тем же загрузчиком классов?

Обновлять

Вот InverseClassLoader:

public class InvertedClassLoader extends URLClassLoader {

private final Set<String> classesToNotDelegate = new HashSet<>();

public InvertedClassLoader(URL... urls) {
    super(urls, Thread.currentThread().getContextClassLoader());
}

public InvertedClassLoader selfLoad(Class<?> classToNotDelegate) {
    classesToNotDelegate.add(classToNotDelegate.getName());
    return this;
}

@Override
public Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
    if (shouldNotDelegate(className)) {
        System.out.println("CHILD LOADER: " + className);
        Class<?> clazz = findClass(className);
        if (resolve) {
            resolveClass(clazz);
        }
        return clazz;
    }
    else {
        System.out.println("PARENT LOADER: " + className);
        return super.loadClass(className, resolve);
    }
}

public <T> Class<T> loadClass(Class<? extends T> classToLoad) throws ClassNotFoundException {
    final Class<?> clazz = loadClass(classToLoad.getName());
    @SuppressWarnings("unchecked")
    final Class<T> castedClass = (Class<T>) clazz;
    return castedClass;
}

private boolean shouldNotDelegate(String className) {
    if (classesToNotDelegate.contains(className) || className.contains("tangosol") ) {
        return true;
    }
    return false;
}

person Dmitry    schedule 18.01.2016    source источник
comment
По вашему коду сложно понять. Можете ли вы дать краткое описание дизайнерской идеи, которую вы пытаетесь реализовать?   -  person Slava Imeshev    schedule 19.01.2016


Ответы (1)


Выпуск 1, часть первая, которую я не могу воспроизвести (см. ниже). Что касается части 2: иерархия загрузчиков классов предназначена для предотвращения исключений «X нельзя преобразовать в X». Но если вы нарушите правило приоритета родителей, у вас могут возникнуть проблемы.

О проблеме 2: установка загрузчика классов контекста потока сама по себе ничего не делает, см. также эту статью (javaworld.com) для дополнительной информации. Кроме того, в отношении проблемы 1, часть 2, цитата из статьи, в которой описывается, что может произойти, если между текущим загрузчиком классов и загрузчиком классов контекста потока нет отношения родитель-потомок:

Помните, что загрузчик классов, который загружает и определяет класс, является частью внутреннего идентификатора JVM для этого класса. Если текущий загрузчик классов загружает класс X, который впоследствии выполняет, скажем, JNDI-поиск некоторых данных типа Y, загрузчик контекста может загрузить и определить Y. Это определение Y будет отличаться от определения с тем же именем, которое видно текущему классу. погрузчик. Введите неясные исключения нарушения ограничений приведения классов и загрузчика.

Ниже приведена простая демонстрационная программа, показывающая, что приведение к интерфейсу из другого загрузчика классов может работать (обратите внимание, что я использую простой проект Java с классами в папке bin и InvertedClassLoader из вашего вопроса в том же (тестовом) пакете ):

import java.io.File;

public class ChildFirstClassLoading {

    public static void main(String[] args) {

        InvertedClassLoader cl = null;
        try {
            File classesDir = new File(new File("./bin").getCanonicalPath());
            System.out.println("Classes dir: " + classesDir);
            cl = new InvertedClassLoader(classesDir.toURI().toURL());
            cl.selfLoad(CTest.class);
            System.out.println("InvertedClassLoader configured.");
            new CTest("Test 1").test();
            ITest t2 = cl.loadClass(CTest.class)
                    .getConstructor(String.class)
                    .newInstance("Test 2");
            t2.test();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cl != null) {
                try { cl.close(); } catch (Exception ignored) {}
            }
        }
    }

    public interface ITest {
        void test();
    }

    public static class CTest implements ITest {

        static {
            System.out.println("CTest initialized.");
        }

        private String s;
        public CTest(String s) {
            this.s = s;
        }
        public void test() {
            System.out.println(s);
        }
    }

}

Если вы измените ITest t2 = на CTest t2 =, вы получите исключение «CTest не может быть приведено к CTest», но использование интерфейса предотвращает это исключение. Поскольку эта небольшая демонстрация работает нормально, я предполагаю, что в вашем приложении происходит что-то еще, что каким-то образом нарушает загрузку классов. Я предлагаю вам работать с ситуацией, когда загрузка классов работает, и продолжать добавлять код, пока он не нарушит загрузку классов.

InvertedClassLoader очень похож на «дочерний первый загрузчик классов», см. этот вопрос для получения хороших ответов, обсуждающих этот способ class- загрузка. Дочерний первый загрузчик классов можно использовать для загрузки «связанных классов» (из вашего третьего выпуска) отдельно. Вы также можете обновить InvertedClassLoader, чтобы всегда «самозагружать» классы в определенных пакетах. И помните, что «как только класс загружается загрузчиком классов, он использует этот загрузчик классов для загрузки всех остальных необходимых ему классов» (цитата из эта статья в блоге).

person vanOekel    schedule 20.01.2016