Мой собственный загрузчик классов? - PullRequest
2 голосов
/ 05 февраля 2009

Вот проблема, с которой я сталкиваюсь. Существует огромное устаревшее приложение, которое работает на Java 1.3 и использует внешний API, скажем, MyAPI v1.0. Точная реализация MyAPI 1.0 находится где-то в пути к классам, используемом приложением. Есть также механизм, который позволяет этому приложению использовать внешний код (своего рода механизм плагинов). Теперь у меня есть другая библиотека Java (MyLib.jar), которая использует MyAPI v2.0 (который НЕ на 100% обратно совместим с v1.0), и я должен использовать ее из исходного приложения, используя этот механизм плагинов. Поэтому я должен как-то позволить двум (несовместимым!) Версиям одного и того же API работать вместе. В частности, я хочу использовать MyAPI v2.0, когда классы API вызываются из классов MyLib.jar, и использовать MyAPI 1.0 во всех других случаях.

MyAPI 1.0 находится в пути к классам, поэтому он будет использоваться по умолчанию, это нормально. Я могу создать свою собственную версию загрузчика классов для загрузки классов из MyAPI 2.0 - нет проблем. Но как мне все это совместить? Вопросы:

  1. Объекты MyLib.jar создают множество (!) Экземпляров классов из MyAPI 2.0. Означает ли это, что мне придется делать ВСЕ эти экземпляры с помощью рефлексии (указывая мой собственный загрузчик классов)? Это адская работа!

  2. Если какой-либо объект MyAPI 2.0 будет создан, и он внутренне создаст другой объект из MyAPI, какой загрузчик классов он будет использовать? Будет ли он использовать мой загрузчик классов или загрузчик по умолчанию?

  3. Как правило, мой подход звучит разумно? Есть ли лучший способ?

Ответы [ 4 ]

3 голосов
/ 05 февраля 2009

Давайте начнем с ответа на ваш 2-й вопрос: при ссылке из какого-либо класса в другой класс он будет загружен тем же загрузчиком классов, который загрузил исходный класс (если, конечно, загрузчик классов не смог найти класс, а затем он будет делегировать его родительскому загрузчику классов).

Сказав это, почему весь ваш MyLib.jar не будет загружен пользовательским загрузчиком классов, а затем он может ссылаться на более новую версию API обычным способом. В противном случае у вас возникнет проблема, потому что вам придется работать с типом объекта и отражением до конца.

2 голосов
/ 05 февраля 2009

Вы должны быть осторожны с загрузчиками классов. Если вы делаете то, что предлагали, вы почти всегда заканчивали MyAPI 1.0 даже при использовании загрузчика классов для MyAPI 2.0. Причина этого в том, как классы загружаются с помощью загрузчика классов. Сначала классы всегда загружаются из загрузчика родительского класса.

"Класс ClassLoader использует модель делегирования для поиска классов и ресурсов. Каждый экземпляр ClassLoader имеет связанный родительский загрузчик классов. При запросе на поиск класса или ресурса экземпляр ClassLoader делегирует поиск класса или ресурса. в родительский загрузчик классов, прежде чем пытаться найти сам класс или ресурс. Встроенный загрузчик классов виртуальной машины, называемый «загрузчик классов начальной загрузки», сам по себе не имеет родителя, но может служить родителем экземпляра ClassLoader ». (http://java.sun.com/javase/6/docs/api/java/lang/ClassLoader.html)

Чтобы обеспечить должную изоляцию между двумя API, вам понадобится 2 загрузчика классов (или 2 в дополнение к основному приложению).

Parent - System classloader
  |- Classloader1 - Used to load MyAPI 1.0
  |- Classloader2 - Used to load MyAPI 2.0

Теперь к вашим вопросам. Вероятно, вы захотите переместить большую часть логики, использующей API, в загрузчики классов. В дополнение к MyAPI 1.0 / 2.0 вам следует загрузить часть приложения, которая их использует. Тогда родительское приложение просто должно вызвать метод, который использует API. Таким образом, вы запускаете одно отражение, чтобы запустить приложение, и все внутри этого приложения просто использует стандартные ссылки.

0 голосов
/ 05 февраля 2009

Это звучит разумно. В [API javadoc для loadClass] [1] говорится:

"Загружает класс с указанным двоичным именем. Реализация этого метода по умолчанию ищет классы в следующем порядке: Вызвать findLoadedClass (String), чтобы проверить, загружен ли уже класс.

Вызвать метод loadClass в загрузчике родительского класса. Если родительский объект имеет значение null, вместо него используется встроенный в виртуальную машину загрузчик классов.

Вызовите метод findClass (String), чтобы найти класс. "

Если CL1 для MyAPI1, CL2 для MyAPI2, а CL3 для MyLib, звучит так, как будто вы хотите, чтобы они проверялись в следующем порядке: CL3, CL2, CL1. Что из приведенной выше цитаты (поскольку родители проверяются первыми) предполагает, что вы хотите, чтобы CL1 имел родительский CL2, а CL2 - родительский CL3. Поскольку конструктор с родительским загрузчиком классов защищен, вам придется использовать URL-загрузчик классов с правильно установленными родителями.

Что-то вроде

URLCLassLoader cl3 = new URLClassLoader(new URL[]{ path to MyLib});
URLCLassLoader cl2 = new URLClassLoader(new URL[]{ path to API2}, cl3);
URLCLassLoader cl1 = new URLClassLoader(new URL[]{ path to API1}, cl2);

тогда везде используйте cl1.

[1]: http://java.sun.com/j2se/1.5.0/docs/api/java/lang/ClassLoader.html#loadClass(java.lang.String, логическое значение)

0 голосов
/ 05 февраля 2009

Вы можете сделать это с причудливым ClassLoader без отражения.

По сути, загрузчик классов должен сказать "если класс загрузки из этого jar, загрузить из classpath B, в противном случае использовать основной ClassLoader".

Это немного сложнее, но если вы начнете с этой идеи, у вас все получится.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...