¿Qué es una dependencia Java "sombreada"?


76

Desarrollador JVM aquí. Últimamente he visto bromas en las salas de chat de IRC e incluso en mi propia oficina sobre las llamadas bibliotecas Java " sombreadas ". El contexto del uso será algo así como:

" Tal y tal proporciona un cliente" sombreado "para XYZ " .

Un ejemplo perfecto es este problema de Jira para HBase : " Publicar un artefacto de cliente con dependencias sombreadas "

Entonces pregunto: ¿Qué es un JAR sombreado , qué significa estar "sombreado"?

Respuestas:


88

El sombreado de dependencias es el proceso de incluir y renombrar dependencias (reubicando las clases y reescribiendo el código de bytes y los recursos afectados) para crear una copia privada que se incluye junto con su propio código .

El concepto generalmente se asocia con uber-jars (también conocidos como frascos gordos ).

Existe cierta confusión sobre el término , debido al complemento de sombra de Maven, que bajo ese solo nombre hace 2 cosas (citando su propia página):

Este complemento proporciona la capacidad de empaquetar el artefacto en un uber-jar, incluidas sus dependencias y sombrear , es decir, cambiar el nombre de los paquetes de algunas de las dependencias.

Por lo tanto, la parte de sombreado es realmente opcional: el complemento permite incluir dependencias en su jarra (jarra de grasa) y, opcionalmente, renombrar (sombrear) las dependencias .

Agregar otra fuente :

Sombrear una biblioteca es tomar los archivos de contenido de dicha biblioteca, ponerlos en su propio jar y cambiar su paquete . Esto es diferente del paquete, que es simplemente enviar los archivos de las bibliotecas al costado de su propio contenedor sin reubicarlos en un paquete diferente.

Técnicamente hablando, las dependencias están sombreadas. Pero es común referirse a las dependencias de un jarro gordo con sombreado como "jar sombreado", y si ese jar es un cliente para otro sistema, puede denominarse "cliente sombreado".

Aquí está el título del problema de Jira para HBase que vinculó en su pregunta:

Publicar un artefacto de cliente con dependencias sombreadas

Entonces, en esta publicación, estoy tratando de presentar los 2 conceptos sin combinarlos.

El bueno

Los tarros Uber a menudo se usan para enviar una aplicación como un solo archivo (lo que facilita su implementación y ejecución). También se pueden usar para enviar bibliotecas junto con algunas (o todas) de sus dependencias sombreadas , a fin de evitar conflictos cuando son utilizadas por otras aplicaciones (que pueden usar diferentes versiones de esas bibliotecas).

Hay varias formas de construir súper jarras, pero maven-shade-pluginva un paso más allá con su función de reubicación de clase :

Si el uber JAR se reutiliza como una dependencia de algún otro proyecto, incluir directamente clases de las dependencias del artefacto en el uber JAR puede causar conflictos de carga de clases debido a clases duplicadas en la ruta de clase. Para abordar este problema, se pueden reubicar las clases que se incluyen en el artefacto sombreado para crear una copia privada de su código de bytes.

(Nota histórica: Jar Jar Links ofreció esa función de reubicación antes)

Entonces, con esto, puede hacer que las dependencias de su biblioteca sean un detalle de implementación , a menos que exponga las clases de esas bibliotecas en su API.

Digamos que tengo un proyecto, ACME Quantanizer ™, que proporciona DecayingSyncQuantanizerclase y depende de Apache commons-rng (porque, por supuesto, para cuantificar adecuadamente necesita un XorShift1024Star, duh).

Si utilizo el complemento shadow maven para producir un uber-jar, y miro dentro, veo estos archivos de clase:

com/acme/DecayingSyncQuantanizer.class
org/apache/commons/rng/RandomProviderState.class
org/apache/commons/rng/RestorableUniformRandomProvider.class
...
org/apache/commons/rng/core/source64/XorShift1024Star.class
org/apache/commons/rng/core/util/NumberFactory.class

Ahora si uso la función de reubicación de clase:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.0.0</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <relocations>
          <relocation>
            <pattern>org.apache.commons</pattern>
            <shadedPattern>com.acme.shaded.apachecommons</shadedPattern>
          </relocation>
        </relocations>
      </configuration>
    </execution>
  </executions>
</plugin>

El contenido de uber-jar se ve así:

com/acme/DecayingSyncQuantanizer.class
com/acme/shaded/apachecommons/rng/RandomProviderState.class
com/acme/shaded/apachecommons/rng/RestorableUniformRandomProvider.class
...
com/acme/shaded/apachecommons/rng/core/source64/XorShift1024Star.class
com/acme/shaded/apachecommons/rng/core/util/NumberFactory.class

No se trata solo de cambiar el nombre de los archivos, sino que reescribe el código de bytes que hace referencia a las clases reubicadas (por lo tanto, mis propias clases y clases commons-rng se transforman).

Además, el complemento Shade también generará un nuevo POM ( dependency-reduced-pom.xml) en el que las dependencias sombreadas se eliminan de la <dependencies>sección. Esto ayuda a usar el jar sombreado como una dependencia para otro proyecto. Entonces puede publicar ese jar en lugar del base, o ambos (usando un calificador para el jar sombreado).

Eso puede ser muy útil ...

El malo

... pero también plantea una serie de problemas. Agregar todas las dependencias en un solo "espacio de nombres" dentro del jar puede volverse desordenado y requerir sombreado y desorden con los recursos.

Por ejemplo: ¿cómo lidiar con los archivos de recursos que incluyen nombres de clases o paquetes? ¿Archivos de recursos como descriptores de proveedores de servicios bajo los cuales viven todos META-INF/services?

El complemento de sombra ofrece transformadores de recursos que pueden ayudar con eso:

Agregar clases / recursos de varios artefactos en un único JAR es sencillo siempre que no haya superposición. De lo contrario, se requiere algún tipo de lógica para fusionar recursos de varios JAR. Aquí es donde entran en acción los transformadores de recursos .

Pero sigue siendo complicado, y los problemas son casi imposibles de anticipar (a menudo se descubren los problemas de la manera más difícil en la producción). Mira por qué detuvimos la construcción de tarros gordos .

En general, implementar un tarro gordo como una aplicación / servicio independiente sigue siendo muy común, solo necesita estar al tanto de las trampas y, para algunos de ellos, es posible que necesite sombreado u otros trucos.

El feo

Hay muchos problemas más difíciles (depuración, comprobabilidad, compatibilidad con OSGi y cargadores de clases exóticos ...).

Pero lo que es más importante, cuando produce una biblioteca, los diversos problemas que pensaba que podía controlar ahora se vuelven infinitamente más complicados, porque su jar se usará en muchos contextos diferentes (a diferencia de un jar gordo que implementa como una aplicación / servicio independiente) en un ambiente controlado).

Por ejemplo, ElasticSearch solía sombrear algunas dependencias en los frascos que enviaban, pero decidieron dejar de hacerlo :

Antes de la versión 2.0, Elasticsearch se proporcionaba como un JAR con algunas (pero no todas) dependencias comunes sombreadas y empaquetadas dentro del mismo artefacto. Esto ayudó a los usuarios de Java que integran Elasticsearch en sus propias aplicaciones para evitar conflictos de versiones de módulos como Guava, Joda, Jackson, etc. Por supuesto, todavía había una lista de otras dependencias no sombreadas como Lucene que aún podrían causar conflictos.
Desafortunadamente, el sombreado es un proceso complejo y propenso a errores que resuelve problemas para algunas personas y crea problemas para otras. El sombreado hace que sea muy difícil para los desarrolladores y autores de complementos escribir y depurar código correctamente porque los paquetes cambian de nombre durante la compilación. Finalmente, solíamos probar Elasticsearch sin sombrear y luego enviar el frasco sombreado, y no nos gusta enviar nada que no estemos probando.
Hemos decidido enviar Elasticsearch sin sombrear desde 2.0 en adelante.

Tenga en cuenta que también se refieren a dependencias sombreadas , no jar sombreado


1
Gracias por tomarse el tiempo para explicar esto. La documentación oficial del complemento de sombra maven es completamente inadecuada y no discute nada de esto, ni siquiera se molesta en definir "uber jar". Esa documentación es obtusa e inútil. Tu escrito es útil.
Cheeso

Excelente explicación, creo que debería incluirse en los documentos oficiales
Adelin

7

Permítanme responder la pregunta con la ayuda del software realmente responsable de crear frascos sombreados ... al menos cuando se usa Maven.

Tomado de la página de inicio del complemento Apache Maven Shade :

Este complemento proporciona la capacidad de empaquetar el artefacto en un uber-jar, incluidas sus dependencias y sombrear, es decir, cambiar el nombre de los paquetes de algunas de las dependencias.

Un jar sombreado, también conocido como uber-jar, también conocido como fat jar, contendrá de forma predeterminada todas las dependencias que se requieren para ejecutar la aplicación Java, de modo que no se requiera ninguna dependencia adicional para estar en el classpath. Solo necesita la versión correcta de Java para ejecutar su aplicación. Un jar sombreado ayudará a evitar problemas de despliegue / classpath, pero será mucho más grande que el jar original de la aplicación y no le ayudará a evitar el infierno del jar.


1
Me temo que esta respuesta es incompleta: explica qué son los tarros gruesos / uber, pero no explica la parte sombreada . Y sí, se supone que el sombreado al 100% ayuda con el "infierno del jarro" (lo que hace que la última parte de esa respuesta sea incorrecta). Por lo tanto, es útil en algún nivel, pero se agrega a la confusión: - /
Hugues M.

1
@HuguesMoreau Puede que no esté 100% completo en mi respuesta, pero aún así trajo el punto que quería aclarar. Gracias por traer la parte faltante a la mesa. El sombreado no evitará el infierno del tarro, eso es lo que quise decir y escribí, pero le dará algunas herramientas disponibles que le permitirán resolver algunos de sus problemas, pero no es automático. Lo que hace que la última parte se lea e interprete de la manera que lo quise decir, al menos está bien. :)
Jesko R.
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.