A cosa serve il plug-in maven-shade e perché dovresti spostare i pacchetti Java?


282

Ho trovato il plug-in maven-shade utilizzato nel pom.xml di qualcuno. Non ho mai usato maven-shade-plugin prima (e sono un Maven n00b), quindi ho cercato di capire il motivo dell'utilizzo di questo e quello che fa.

Ho esaminato i documenti di Maven , tuttavia non riesco a capire questa affermazione:

"Questo plugin offre la possibilità di impacchettare il manufatto in un Uber-Jar, comprese le sue dipendenze e di ombreggiare - ad esempio rinominare - i pacchetti di alcune dipendenze."

La documentazione sulla pagina non sembra molto adatta ai principianti.

Che cos'è un "Uber Jar?" Perché qualcuno dovrebbe volerne fare uno? Qual è lo scopo di rinominare i pacchetti delle dipendenze? Ho provato ad esaminare gli esempi nella pagina apache di maven-shade-plugin come "Selezione dei contenuti per Uber Jar", ma non riesco ancora a capire cosa si sta realizzando con "shading".

Qualsiasi suggerimento su esempi / casi d'uso illustrativi (con una spiegazione del perché in questo caso è stata richiesta l'ombreggiatura - quale problema sta risolvendo) sarebbe apprezzato. Infine, quando dovrei usare il plugin maven-shade?


16
Per la denominazione "uber jar" ho l'unica associazione con il tedesco, dove "über" significa "sopra". Insieme letteralmente significa "vasetti sopra tutti gli altri". "Ombreggiatura" equivale a "riallocazione del pacchetto" necessaria per le classi o le risorse che si scontrano.
dma_k,

1
s/reallocation/relocation/nel commento sopra.
Christopher Schultz,

12
Il barattolo superbo è proprio come un anello: un barattolo per domarli tutti, un barattolo per trovarli, un barattolo per portarli tutti e nell'oscurità li legano.
Marti Nito,

Risposte:


343

Uber JAR, in breve, è un JAR che contiene tutto.

Normalmente a Maven, facciamo affidamento sulla gestione delle dipendenze. Un artefatto contiene solo le classi / risorse di se stesso. Maven sarà responsabile di scoprire tutti gli artefatti (JAR ecc.) Che il progetto dipende da quando il progetto è stato costruito.

Un uber-jar è qualcosa che prende tutte le dipendenze, estrae il contenuto delle dipendenze e le mette con le classi / risorse del progetto stesso, in un unico grande JAR. Avere un tale super-jar, è facile per l'esecuzione, perché avrai bisogno di un solo JAR grande invece di tonnellate di piccoli JAR per eseguire la tua app. In alcuni casi facilita anche la distribuzione.

Solo una nota a margine. Evita di usare uber-jar come dipendenza di Maven, poiché sta rovinando la funzione di risoluzione delle dipendenze di Maven. Normalmente creiamo uber-jar solo per l'artefatto finale per la distribuzione effettiva o per la distribuzione manuale, ma non per il repository Maven.


Aggiornamento: ho appena scoperto di non aver risposto a una parte della domanda: "Qual è lo scopo di rinominare i pacchetti delle dipendenze?". Ecco alcuni brevi aggiornamenti e, si spera, aiuteranno le persone con domande simili.

La creazione di uber-jar per facilitare la distribuzione è un caso d'uso del plugin ombra. Esistono anche altri casi d'uso comuni che comportano la ridenominazione dei pacchetti.

Ad esempio, sto sviluppando una Foolibreria, che dipende da una versione specifica (ad es. 1.0) della Barlibreria. Supponendo che non sia possibile utilizzare altre versioni di Barlib (a causa della modifica dell'API o di altri problemi tecnici, ecc.). Se io dichiaro semplicemente Bar:1.0come Foo's dipendenza nel Maven, è possibile cadere in un problema: un Quxprogetto è in funzione Foo, e anche Bar:2.0(e non si può usare Bar:1.0, perché Quxha bisogno di usare nuova funzione Bar:2.0). Ecco il dilemma: dovrebbe Quxusare Bar:1.0(quale Quxcodice non funzionerà) o Bar:2.0(quale Foocodice non funzionerà)?

Per risolvere questo problema, lo sviluppatore di Foopuò scegliere di utilizzare il plugin ombra per rinominarne l'utilizzo Bar, in modo che tutte le classi in Bar:1.0jar siano incorporate in Foojar e il pacchetto delle Barclassi incorporate venga modificato da com.bara com.foo.bar. In questo modo, Quxpuò tranquillamente dipendere dal Bar:2.0fatto che ora Foonon dipende più da Bare sta usando la propria copia di "alterato" che si Bartrova in un altro pacchetto.


5
Grazie, aiuta molto. Si chiama "shading" perché nasconde le dipendenze e altri barattoli all'interno del barattolo di vetro?
essendo il

6
Non sono davvero sicuro del motivo dietro al nome, ma dalla sua prima pagina sembra che lo "shading" descriva il "nascondersi" delle dipendenze. Poiché è possibile includere alcune dipendenze nel JAR ombreggiato, il plug-in di ombra genererà anche un POM corretto per l'utente che rimuoverà le dipendenze incluse. Sembra che l'ombreggiatura descriva questo processo
Adrian Shum,

1
@AdrianShum: puoi suggerire come creare un vaso Uber, senza usare il plugin ombra? O in particolare come risolvere la "rovina della funzione di risoluzione delle dipendenze di Maven"?
Suraj Menon,

3
@SurajMenon: l'utilizzo del plug-in Shade è il modo più semplice per creare uber-jar. Tuttavia, è anche possibile utilizzare il plug-in Assembly e utilizzare il jar-with-dependencydescrittore incorporato . Per problemi di dipendenza da roslution usando uber-jar, ho già menzionato nella mia risposta: non usare uber-jar come dipendenza, punto. Un po 'più in dettaglio: prima di creare l'Uber-Jar, dovresti avere un progetto normale con dipendenza normale. Quel manufatto originale è quello che dovresti usare come dipendenza (invece del barattolo di vetro)
Adrian Shum,

1
Solo una domanda minore: smette Class.forNamedi funzionare?
SOF

64

Di recente mi chiedevo perché elasticsearch offusca e trasferisca alcune (ma non tutte) le sue dipendenze. Ecco una spiegazione del manutentore del progetto, @kimchy :

La parte shading è intenzionale, le librerie shaded che usiamo in elasticsearch sono a tutti gli effetti parte di elasticsearch, la versione utilizzata è strettamente legata a ciò che espone elasticsearch e come usa la libreria in base agli interni di come funziona la libreria (e che cambia tra le versioni), netty e guava sono ottimi esempi.

A proposito, non ho alcun problema a fornire effettivamente diversi barattoli di elasticsearch, uno con lucene non ombreggiato e uno con Lucene ombreggiato. Non sono sicuro di come farlo con Maven. Non voglio fornire una versione che non offra netty / jackson per esempio, a causa dell'uso intimo e profondo che elasticsearch ha con sé (ad esempio, usando l'imminente miglioramento del bufferring con qualsiasi versione precedente di netty tranne quella attuale effettivamente utilizza più memoria rispetto all'uso considerevolmente inferiore).

- https://github.com/elasticsearch/elasticsearch/issues/2091#issuecomment-7156766

E un altro qui da Drewr :

L'ombreggiatura è importante per mantenere le nostre dipendenze (in particolare netty, lucene, guava) vicine al nostro codice in modo da poter risolvere un problema anche se il fornitore a monte è in ritardo. È possibile che distribuiremo versioni modulari del codice, il che aiuterebbe con il tuo problema specifico (ad esempio # 2091), ma al momento non possiamo semplicemente rimuovere le dipendenze ombreggiate. Puoi creare una versione locale di ES per i tuoi scopi fino a quando non ci sarà una soluzione migliore.

- https://github.com/elasticsearch/elasticsearch/pull/3244#issuecomment-20125452

Quindi, questo è un caso d'uso. Come per un esempio illustrativo, di seguito è riportato come viene utilizzato maven-shade-plugin nel pom.xml di elasticsearch (v0.90.5). Le artifactSet::includelinee indicano che quali dipendenze inserire nel JAR super (in pratica, sono decompresse e reimballate insieme alle stesse classi di elasticsearch quando viene prodotto il vaso target di elasticsearch. (Nel caso in cui non lo sapessi già, un file JAR è solo un file ZIP che contiene le classi, le risorse, ecc. del programma e alcuni metadati. Puoi estrarne uno per vedere come viene assemblato.)

Le relocations::relocationlinee sono simili, tranne per il fatto che in ogni caso applicano anche le sostituzioni specificate alle classi della dipendenza - in questo caso, portandole sotto org.elasticsearch.common.

Infine, la filterssezione esclude alcune cose dal JAR di destinazione che non dovrebbero essere presenti - come metadati JAR, file di build ant, file di testo, ecc. Che sono impacchettati con alcune dipendenze, ma che non appartengono a un JAR superbo.

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.1</version>
        <executions>
            <execution>
                <phase>package</phase>
                <goals>
                    <goal>shade</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <minimizeJar>true</minimizeJar>
            <artifactSet>
                <includes>
                    <include>com.google.guava:guava</include>
                    <include>net.sf.trove4j:trove4j</include>
                    <include>org.mvel:mvel2</include>
                    <include>com.fasterxml.jackson.core:jackson-core</include>
                    <include>com.fasterxml.jackson.dataformat:jackson-dataformat-smile</include>
                    <include>com.fasterxml.jackson.dataformat:jackson-dataformat-yaml</include>
                    <include>joda-time:joda-time</include>
                    <include>io.netty:netty</include>
                    <include>com.ning:compress-lzf</include>
                </includes>
            </artifactSet>
            <relocations>
                <relocation>
                    <pattern>com.google.common</pattern>
                    <shadedPattern>org.elasticsearch.common</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>gnu.trove</pattern>
                    <shadedPattern>org.elasticsearch.common.trove</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>jsr166y</pattern>
                    <shadedPattern>org.elasticsearch.common.util.concurrent.jsr166y</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>jsr166e</pattern>
                    <shadedPattern>org.elasticsearch.common.util.concurrent.jsr166e</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>org.mvel2</pattern>
                    <shadedPattern>org.elasticsearch.common.mvel2</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>com.fasterxml.jackson</pattern>
                    <shadedPattern>org.elasticsearch.common.jackson</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>org.joda</pattern>
                    <shadedPattern>org.elasticsearch.common.joda</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>org.jboss.netty</pattern>
                    <shadedPattern>org.elasticsearch.common.netty</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>com.ning.compress</pattern>
                    <shadedPattern>org.elasticsearch.common.compress</shadedPattern>
                </relocation>
            </relocations>
            <filters>
                <filter>
                    <artifact>*:*</artifact>
                    <excludes>
                        <exclude>META-INF/license/**</exclude>
                        <exclude>META-INF/*</exclude>
                        <exclude>META-INF/maven/**</exclude>
                        <exclude>LICENSE</exclude>
                        <exclude>NOTICE</exclude>
                        <exclude>/*.txt</exclude>
                        <exclude>build.properties</exclude>
                    </excludes>
                </filter>
            </filters>
        </configuration>
    </plugin>
</plugins>

2

Piccolo avvertimento

Sebbene questo non descriva il motivo per cui si vorrebbe usare il plug-in maven-shade (poiché la risposta selezionata lo descrive abbastanza bene), vorrei notare che ho avuto problemi con esso. Ha cambiato il JAR (dal momento che quello che sta facendo) e ha causato la regressione nel mio software.

Quindi, invece di usare questo (o il plugin maven-jarjar), ho usato il binario di JarJar che sembra funzionare senza problemi.

Sto pubblicando qui la mia soluzione poiché mi ci è voluto del tempo per trovare una soluzione decente.


Scarica il file JAR di JarJar

Puoi scaricare il vaso da qui: https://code.google.com/p/jarjar/ Nel menu a sinistra hai un link per scaricarlo.


Come utilizzare JarJar per spostare le classi di un JAR da un pacchetto all'altro

In questo esempio cambieremo il pacchetto da "com.fasterxml.jackson" a "io.kuku.dependencies.com.fasterxml.jackson". - Il JAR di origine si chiama "jackson-databind-2.6.4.jar" e il nuovo JAR modificato (di destinazione) si chiama "kuku-jackson-databind-2.6.4.jar". - Il file JAR "jarjar" è nella versione 1.4

  1. Crea un file "rules.txt". Il contenuto del file dovrebbe essere (guarda il periodo precedente al carattere "@"): regola com.fasterxml.jackson. ** io.kuku.dependencies.com.fasterxml.jackson. @ 1

  2. Eseguire il comando seguente: java -jar jarjar-1.4.jar process rules.txt jackson-databind-2.6.4.jar kuku-jackson-databind-2.6.4.jar


Installazione dei JAR modificati nel repository locale

In questo caso sto installando 3 file che si trovano nella cartella "c: \ my-jars \".

mvn install: install-file -Dfile = C: \ my-jars \ kuku-jackson-annotations-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-annotations -Dversion = 2.6.4 - Dpackaging = vaso

mvn install: install-file -Dfile = C: \ my-jars \ kuku-jackson-core-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-core -Dversion = 2.6.4 - Dpackaging = vaso

mvn install: install-file -Dfile = C: \ my-jars \ kuku-jackson-databind-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-annotations -Dversion = 2.6.4 - Dpackaging = vaso


Utilizzando i JAR modificati nel pom del progetto

In questo esempio, questo è l'elemento "dipendenze" nel progetto pom:

<dependencies>
    <!-- ================================================== -->
    <!-- kuku JARs -->
    <!-- ================================================== -->
    <dependency>
        <groupId>io.kuku.dependencies</groupId>
        <artifactId>kuku-jackson-annotations</artifactId>
        <version>2.6.4</version>
    </dependency>
    <dependency>
        <groupId>io.kuku.dependencies</groupId>
        <artifactId>kuku-jackson-core</artifactId>
        <version>2.6.4</version>
    </dependency>
    <dependency>
        <groupId>io.kuku.dependencies</groupId>
        <artifactId>kuku-jackson-databind</artifactId>
        <version>2.6.4</version>
    </dependency>
</dependencies>

1
Grazie per questo suggerimento alternativo. Non era quello che cercavo, ma si è rivelata una soluzione molto più semplice e rapida per applicare traduzioni di pacchetti una tantum su librerie legacy che non cambieranno mai.
Frelling

hai capito il motivo di tali guasti confrontando il contenuto dei rispettivi risultati?
tribbloid,

2

Penso che un esempio della necessità di un vaso "ombreggiato" sia una funzione AWS Lambda. Sembrano lasciarti caricare solo 1 vaso, non un'intera raccolta di .jar come potresti trovare in un tipico file .war. Quindi, creare un singolo .jar con tutte le dipendenze del progetto ti consente di farlo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.