2009-02-05 9 views
2

Вот проблема, с которой я сталкиваюсь. Существует огромное унаследованное приложение, работающее на 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. Как правило, мой подход звучит разумно? Есть ли способ лучше?

ответ

3

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

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

2

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

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

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

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

«Классы всегда загружаются с родительского загрузчика классов». Правило может быть разбито пользовательским загрузчиком классов. С осторожностью. – Darron

0

Вы можете сделать это с помощью фантастического ClassLoader без отражения.

В принципе, загрузчик классов должен сказать: «Если класс загрузки находится из этой банки, загрузите из пути класса B, в противном случае используйте основной ClassLoader».

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

0

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

«Загружает класс с указанным двоичным именем. По умолчанию реализация этого метода выполняет поиск классов в следующем порядке: Invoke findLoadedClass (String) для проверки если класс уже загружен.

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

Призовите 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, булево)

Смежные вопросы