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-plugin
va 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 DecayingSyncQuantanizer
clase 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