Développeur JVM ici. Dernièrement, j'ai vu des plaisanteries sur IRC chat rooms et même dans mon propre bureau à propos de soi-disant " ombré " Java bibliothèques. Le contexte d'utilisation sera quelque chose comme:
" Tel ou tel fournit un client" ombré "pour XYZ."
L'exemple parfait est ce problème Jira pour HBase : " Publier un artefact client avec des dépendances ombrées"
Je demande donc: Qu'est-ce qu'un JAR ombré, qu'est-ce que cela signifie d'être "ombré"?
Ombrage des dépendances est le processus consistant à inclure et renommer les dépendances (déplaçant ainsi les classes et réécrivant le bytecode et les ressources affectés) vers créez une copie privée que vous regroupez avec votre propre code .
Le concept est généralement associé à ber-jars (aka fat jars ).
Il y a une certaine confusion sur le terme , à cause du plugin maven shadow qui, sous ce nom unique, fait 2 choses (en citant leur propre page):
Ce plugin offre la possibilité de conditionner l'artefact dans un uber-jar, y compris ses dépendances et de shadow - c'est-à-dire renommer - les packages de certaines des dépendances.
Donc la partie shading est en fait optionnelle: le plugin permet d'inclure des dépendances dans votre jar (fat jar), et éventuellement de renommer (shadow) les dépendances .
Ajout ne autre source :
Ombrer une bibliothèque, c'est prendre les fichiers de contenu de cette bibliothèque, les mettre dans votre propre pot, et changer leur paquet . C'est différent de l'emballage qui expédie simplement les fichiers de bibliothèques dans votre propre pot sans les déplacer dans un autre paquet.
Techniquement parlant, les dépendances sont grisées. Mais il est courant de faire référence à un gros pot avec des dépendances ombrées comme "pot ombré", et si ce pot est un client pour un autre système, il peut être appelé "client ombré".
Voici le title du numéro Jira pour HBase que vous avez lié dans votre question:
Publier un artefact client avec des dépendances ombrées
Donc, dans ce post, j'essaie de présenter les 2 concepts sans les confondre.
Les uber-jars sont souvent utilisés pour expédier une application en tant que fichier unique (ce qui facilite son déploiement et son exécution). Ils peuvent également être utilisés pour expédier des bibliothèques avec certaines (ou toutes) de leurs dépendances ombrées, afin d'éviter les conflits lorsqu'elles sont utilisées par d'autres applications (qui pourraient utiliser différentes versions de ces bibliothèques).
Il existe plusieurs façons de créer des uber-jars, mais maven-shade-plugin
va plus loin avec sa fonction relocalisation de classe :
Si l'uber JAR est réutilisé en tant que dépendance d'un autre projet, l'inclusion directe des classes des dépendances de l'artefact dans l'uber JAR peut provoquer des conflits de chargement de classe en raison de classes en double sur le chemin de classe. Pour résoudre ce problème, on peut déplacer les classes qui sont incluses dans l'artefact ombré afin de créer une copie privée de leur bytecode.
(Note historique: Liens Jar Jar offert cette fonctionnalité de relocalisation avant)
Ainsi, avec cela, vous pouvez faire de vos dépendances de bibliothèque un détail d'implémentation , sauf si vous exposez des classes de ces bibliothèques dans votre API.
Disons que j'ai un projet, ACME Quantanizer ™, qui fournit la classe DecayingSyncQuantanizer
, et dépend d'Apache commons-rng (car bien sûr pour quantifier correctement, vous avez besoin d'un XorShift1024Star
, duh).
Si j'utilise le plugin shadow maven pour produire un uber-jar, et que je regarde à l'intérieur, je vois ces fichiers de classe:
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
Maintenant, si j'utilise la fonction de relocalisation de classe:
<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>
Le contenu de uber-jar ressemble à ceci:
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
Il ne s'agit pas seulement de renommer des fichiers, il réécrit le bytecode qui fait référence aux classes déplacées (donc, mes propres classes et classes commons-rng sont toutes transformées).
De plus, le plugin Shade générera également un nouveau POM (dependency-reduced-pom.xml
) dans lequel les dépendances ombrées sont supprimées du <dependencies>
section. Cela permet d'utiliser le pot ombré comme dépendance pour un autre projet. Vous pouvez donc publier ce pot au lieu du pot de base, ou les deux (en utilisant un qualificatif pour le pot ombré).
Cela peut donc être très utile ...
... mais cela pose également un certain nombre de problèmes. L'agrégation de toutes les dépendances dans un seul "espace de noms" dans le bocal peut devenir compliqué et nécessiter un ombrage et une confusion avec les ressources.
Par exemple: comment gérer les fichiers de ressources qui incluent des noms de classe ou de package? Fichiers de ressources tels que les descripteurs de fournisseurs de services qui vivent tous sous META-INF/services
?
Le plugin d'ombre offre transformateurs de ressources qui peuvent aider à cela:
L'agrégation de classes/ressources de plusieurs artefacts en un seul JAR uber est simple tant qu'il n'y a pas de chevauchement. Sinon, une sorte de logique pour fusionner les ressources de plusieurs fichiers JAR est requise. C'est là que transformateurs de ressources interviennent.
Mais c'est toujours compliqué et les problèmes sont presque impossibles à prévoir (vous découvrez souvent les problèmes à la dure en production). Voir pourquoi-nous-avons-cessé de construire des pots de graisse .
Dans l'ensemble, le déploiement d'un gros pot en tant qu'application/service autonome est encore très courant, il vous suffit d'être conscient des pièges, et pour certains, vous pourriez avoir besoin - ombrage ou d'autres astuces .
Il y a beaucoup plus de problèmes difficiles (débogage, testabilité, compatibilité avec OSGi et chargeurs de classe exotiques ...).
Mais plus important encore, lorsque vous produisez une bibliothèque, les différents problèmes que vous pensiez pouvoir contrôler deviennent maintenant infiniment plus compliqués, car votre jar sera utilisé dans de nombreux contextes différents (contrairement à un gros jar que vous déployez en tant qu'application/service autonome dans un environnement contrôlé).
Par exemple, ElasticSearch avait l'habitude de masquer certaines dépendances dans les pots qu'ils livraient, mais ils ont décidé d'arrêter de le faire :
Avant la version 2.0, Elasticsearch était fourni sous forme de fichier JAR avec certaines (mais pas toutes) des dépendances communes ombrées et regroupées dans le même artefact. Cela a aidé Java qui intègrent Elasticsearch dans leurs propres applications à éviter les conflits de version de modules comme Guava, Joda, Jackson, etc. Bien sûr, il y avait encore une liste d'autres dépendances non ombrées comme Lucene qui pourrait encore provoquer des conflits.
Malheureusement, l'ombrage est un processus complexe et sujet aux erreurs qui a résolu des problèmes pour certaines personnes tout en créant des problèmes pour d'autres. L'ombrage rend très difficile pour les développeurs et les auteurs de plugins d'écrire et de déboguer le code correctement car les packages sont renommés lors de la construction. Enfin, nous avions l'habitude de tester Elasticsearch non ombré puis d'expédier le pot ombragé, et nous n'aimons pas expédier tout ce que nous ne testons pas.
Nous avons décidé d'expédier Elasticsearch sans ombrage à partir de 2.0.
Veuillez noter qu'ils font également référence à dépendances ombrées, pas pot ombré
Permettez-moi de répondre à la question à l'aide du logiciel réellement responsable de la création de pots ombrés ... du moins lors de l'utilisation de maven.
Extrait de la page d'accueil Apache Maven Shade Plugin :
Ce plugin offre la possibilité de conditionner l'artefact dans un uber-jar, y compris ses dépendances et de masquer - c'est-à-dire de renommer - les packages de certaines des dépendances.
Un pot ombré aka uber-jar aka fat jar contiendra par défaut toutes les dépendances requises pour exécuter l'application Java afin qu'aucune dépendance supplémentaire ne soit requise pour être dans le chemin de classe. Vous avez seulement besoin de la correct Java version pour exécuter votre application. Un pot ombré aidera à éviter les problèmes de déploiement/chemin de classe, mais il sera beaucoup plus grand que le pot d'application d'origine et ne vous aidera pas à éviter l'enfer des pots .