Le dipendenze di ombreggiatura sono il processo di inclusione e ridenominazione delle dipendenze (in tal modo riposizionando le classi e riscrivendo il codice e le risorse interessati) per creare una copia privata raggruppata insieme al proprio codice .
Il concetto è di solito associato a uber-jars (aka vasetti di grasso ).
C'è un po 'di confusione sul termine , a causa del plug-in Maven Shadow, che sotto quel singolo nome fa 2 cose (citando la propria pagina):
Questo plug-in offre la possibilità di impacchettare il manufatto in un Uber-Jar, comprese le sue dipendenze e di ombreggiare - cioè rinominare - i pacchetti di alcune dipendenze.
Quindi la parte shading è in realtà facoltativa: il plug-in consente di includere dipendenze nel tuo jar (fat jar) e facoltativamente rinominare (shading) dipendenze .
Aggiunta di un'altra fonte :
Ombreggiare una libreria significa prendere i file dei contenuti di detta libreria, metterli nel proprio vaso e cambiare il loro pacchetto . Questo è diverso dal packaging che sta semplicemente spedendo i file delle librerie nel tuo vaso senza trasferirli in un pacchetto diverso.
Tecnicamente parlando, le dipendenze sono ombreggiate. Ma è comune fare riferimento a un fat-jar-with-shaded-dependances come "jar ombreggiato", e se quel jar è un client per un altro sistema, può essere indicato come "client ombreggiato".
Ecco il titolo del problema di Jira per HBase che hai collegato nella tua domanda:
Pubblica un artefatto client con dipendenze ombreggiate
Quindi in questo post sto cercando di presentare i 2 concetti senza confonderli.
Il bene
Uber-jars sono spesso usati per spedire un'applicazione come un singolo file (facilita la distribuzione e l'esecuzione). Possono anche essere usati per spedire librerie insieme ad alcune (o tutte) delle loro dipendenze ombreggiate , al fine di evitare conflitti quando usate da altre applicazioni (che potrebbero usare versioni diverse di quelle librerie).
Esistono diversi modi per costruire uber-jars, ma maven-shade-plugin
va oltre con la sua funzione di trasferimento di classe :
Se il JAR superiore viene riutilizzato come dipendenza di qualche altro progetto, l'inclusione diretta delle classi dalle dipendenze del manufatto nel JAR superiore può causare conflitti di caricamento delle classi a causa di classi duplicate sul percorso della classe. Per risolvere questo problema, è possibile riposizionare le classi che vengono incluse nell'artefatto ombreggiato al fine di creare una copia privata del loro bytecode.
(Nota storica: Jar Jar Links ha offerto prima quella funzione di trasferimento)
Quindi con questo puoi rendere le dipendenze delle tue librerie un dettaglio di implementazione , a meno che tu non esponga le classi da quelle librerie nella tua API.
Diciamo che ho un progetto, ACME Quantanizer ™, che fornisce DecayingSyncQuantanizer
classe e dipende da Apache commons-rng (perché ovviamente per quantanizzare correttamente è necessario un XorShift1024Star
, duh).
Se uso il plugin ombra maven per produrre un Uber-Jar e guardo dentro, vedo questi file di 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
Ora se uso la funzione di trasferimento di 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>
Il contenuto di uber-jar è simile al seguente:
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
Non si tratta solo di rinominare i file, ma riscrive il bytecode che fa riferimento a classi trasferite (quindi, le mie classi e le mie classi comuni sono tutte trasformate).
Inoltre, il plugin Shade genererà anche un nuovo POM ( dependency-reduced-pom.xml
) in cui le dipendenze ombreggiate vengono rimosse dalla <dependencies>
sezione. Questo aiuta a usare il vaso ombreggiato come dipendenza per un altro progetto. Quindi puoi pubblicare quel vaso anziché quello di base o entrambi (usando un qualificatore per il vaso ombreggiato).
Quindi può essere molto utile ...
Il cattivo
... ma pone anche una serie di problemi. L'aggregazione di tutte le dipendenze in un singolo "spazio dei nomi" all'interno del barattolo può diventare confusa e richiedere ombre e confusione con le risorse.
Ad esempio: come gestire i file di risorse che includono nomi di classi o pacchetti? File di risorse come descrittori di provider di servizi che vivono tutti META-INF/services
?
Il plugin ombra offre trasformatori di risorse che possono aiutare in questo:
L'aggregazione di classi / risorse da diversi artefatti in un unico JAR è semplice fintanto che non vi sono sovrapposizioni. Altrimenti, è necessario un qualche tipo di logica per unire le risorse da diversi JAR. È qui che entrano in gioco i trasformatori di risorse .
Ma è ancora disordinato e i problemi sono quasi impossibili da prevedere (abbastanza spesso scopri i problemi nel modo più difficile in produzione). Scopri perché abbiamo smesso di costruire vasetti grassi .
Tutto sommato, la distribuzione di un grosso vaso come app / servizio autonomo è ancora molto comune, devi solo essere consapevole dei gotcha e, per alcuni di questi, potresti aver bisogno di ombre o altri trucchi.
Il brutto
Ci sono molti problemi più difficili (debug, testabilità, compatibilità con OSGi e classloader esotici ...).
Ma ancora più importante, quando produci una libreria, i vari problemi che pensavi di poter controllare ora diventano infinitamente più complicati, perché il tuo jar verrà utilizzato in molti contesti diversi (a differenza di un fat jar che distribuisci come app / servizio autonomo in un ambiente controllato).
Ad esempio, ElasticSearch era solito ombreggiare alcune dipendenze nei barattoli che spedivano, ma hanno deciso di smettere di farlo :
Prima della versione 2.0, Elasticsearch era fornito come JAR con alcune (ma non tutte) dipendenze comuni ombreggiate e impacchettate all'interno dello stesso artefatto. Ciò ha aiutato gli utenti Java che hanno incorporato Elasticsearch nelle proprie applicazioni per evitare conflitti di versione di moduli come Guava, Joda, Jackson, ecc. Naturalmente, c'era ancora un elenco di altre dipendenze non ombreggiate come Lucene che potevano ancora causare conflitti.
Sfortunatamente, l'ombreggiatura è un processo complesso e soggetto a errori che ha risolto problemi per alcune persone, creando problemi per altri. L'ombreggiatura rende molto difficile per gli sviluppatori e gli autori di plug-in scrivere e eseguire il debug del codice correttamente perché i pacchetti vengono rinominati durante la compilazione. Alla fine, abbiamo usato per testare Elasticsearch non ombreggiato, quindi spedire il barattolo ombreggiato e non ci piace spedire nulla che non stiamo testando.
Abbiamo deciso di spedire Elasticsearch senza ombreggiatura dalla 2.0 in poi.
Si noti che si riferiscono anche a dipendenze ombreggiate , non a vaso ombreggiato