Java, Classpath, Classloading => Varias versiones del mismo jar / proyecto

Sé que esta puede ser una pregunta tonta para codificadores experimentados. Pero tengo una biblioteca (un cliente http) que requieren algunos de los otros frameworks / jar usados ​​en mi proyecto. Pero todos ellos requieren diferentes versiones principales como:

httpclient-v1.jar => Required by cralwer.jar httpclient-v2.jar => Required by restapi.jar httpclient-v3.jar => required by foobar.jar 

¿Es el cargador de clases lo suficientemente inteligente como para separarlos de alguna manera? ¿Probablemente no? ¿Cómo maneja esto el Classloader? En caso de que una clase sea la misma en los tres jar. ¿Cuál está cargado y por qué?

¿El Classloader solo recupera exactamente un jar o mezcla clases arbitrariamente? Entonces, por ejemplo, si una clase se carga desde la Versión-1.jar, todas las demás clases cargadas desde el mismo cargador de clases entrarán todas en el mismo contenedor.

¿Cómo manejas este problema?

¿Hay algún truco para “incorporar” los flasks en “required.jar” para que el Classloader los vea como “una unidad / paquete” o de alguna manera se vinculen?

Los problemas relacionados con Classloader son bastante complejos. En cualquier caso, debe tener en cuenta algunos hechos:

  • Los cargadores de clase en una aplicación generalmente son más que uno solo. El cargador de clase bootstrap delega en el apropiado. Cuando instancia una nueva clase, se invoca el cargador de clases más específico. Si no encuentra una referencia a la clase que está intentando cargar, delega en su elemento principal, y así sucesivamente, hasta que llegue al cargador de clases de arranque. Si ninguno de ellos encuentra una referencia a la clase que está intentando cargar, obtendrá una ClassNotFoundException.

  • Si tiene dos clases con el mismo nombre binario, puede buscarlas en el mismo cargador de clases y desea saber cuál de ellas está cargando, solo puede inspeccionar la forma en que el cargador de clases específico intenta resolver un nombre de clase.

  • De acuerdo con la especificación del lenguaje Java, no existe una restricción de exclusividad para un nombre binario de clase, pero hasta donde puedo ver, debe ser único para cada cargador de clases.

Puedo encontrar una manera de cargar dos clases con el mismo nombre binario, y se trata de cargarlas (y todas sus dependencias) mediante dos cargadores de clases diferentes que anulan el comportamiento predeterminado. Un ejemplo aproximado:

  ClassLoader loaderA = new MyClassLoader(libPathOne); ClassLoader loaderB = new MyClassLoader(libPathTwo); Object1 obj1 = loaderA.loadClass("first.class.binary.name", true) Object2 obj2 = loaderB.loadClass("second.class.binary.name", true); 

Siempre encontré la personalización del clasificador como una tarea difícil. Prefiero sugerir evitar varias dependencias incompatibles si es posible.

Cada carga de clases elige exactamente una clase. Por lo general, el primero encontrado.

OSGi tiene como objective resolver el problema de las versiones múltiples de la misma jarra. Equinox y Apache Felix son las implementaciones de código abierto más comunes para OSGi.

Classloader cargará primero las clases del jar que estaba en el classpath. Normalmente, las versiones incompatibles de la biblioteca tendrán una diferencia en los paquetes, pero en el caso improbable que realmente sean incompatibles y no puedan ser reemplazados por uno – intente con jarjar.

Los cargadores de clases cargan la clase a pedido. Esto significa que la clase requerida primero por su aplicación y las bibliotecas relacionadas se cargarán antes que otras clases; la solicitud para cargar las clases dependientes normalmente se emite durante el proceso de carga y vinculación de una clase dependiente.

Es probable que encuentre LinkageError s que indica que las definiciones de clase duplicadas se han encontrado para cargadores de clases. Normalmente no intentan determinar qué clase se debe cargar primero (si hay dos o más clases del mismo nombre en la ruta de clase del cargador). Algunas veces, el cargador de clases cargará la primera clase que ocurre en el classpath e ignorará las clases duplicadas, pero esto depende de la implementación del cargador.

La práctica recomendada para resolver este tipo de errores es utilizar un cargador de clases separado para cada conjunto de bibliotecas que tienen dependencias conflictivas. De esta forma, si un cargador de clases intenta cargar clases desde una biblioteca, las mismas se cargarían con el mismo cargador de clases que no tiene acceso a otras bibliotecas y dependencias.