Maven-shade-plugin ne için kullanılır ve neden Java paketlerini yeniden konumlandırmak istersiniz?


282

Maven-shade-plugin'in birinin pom.xml dosyasında kullanıldığını gördüm. Daha önce hiç maven-shade-plugin kullanmadım (ve ben bir Maven n00b) bu ​​yüzden bunu kullanmanın nedenini ve ne yaptığını anlamaya çalıştım.

Maven belgelerine baktım , ancak bu ifadeyi anlayamıyorum:

"Bu eklenti, eserler bağımlılıkları da dahil olmak üzere bir uber kavanozuna paketlenebilme ve bazı bağımlılıkların paketlerini gölgelendirebilme - yani yeniden adlandırabilme - olanağı sağlıyor."

Sayfadaki belgeler çok yeni kullanıcı dostu görünmüyor.

"Uber kavanoz" nedir? Neden biri yapmak ister ki? Bağımlılıkların paketlerini yeniden adlandırmanın anlamı nedir? "Uber Jar için içerik seçme" gibi maven-shade-plugin apache sayfasındaki örnekleri gözden geçirmeye çalıştım, ancak yine de "gölgeleme" ile neler yapıldığını anlayamıyorum.

Açıklayıcı örneklere / kullanım örneklerine (bu durumda gölgelemenin neden gerekli olduğuna dair bir açıklama ile - hangi sorunu çözdüğü) bir açıklama takdir edilecektir. Son olarak, maven-shade-eklentisini ne zaman kullanmalıyım?


16
"Uber jar" isminde "über" in "over" anlamına geldiği tek Almanca dernek var. Birlikte kelimenin tam anlamıyla "tüm kavanozlar üzerinde kavanoz" anlamına gelir. "Gölgeleme", çarpışan sınıflar veya kaynaklar için gereken "paket yeniden tahsisi" ile aynıdır.
dma_k

1
s/reallocation/relocation/yukarıdaki yorumda.
Christopher Schultz

12
Uber kavanoz tek halka gibidir: Hepsine hükmedecek bir kavanoz, onları bulmak için bir kavanoz, hepsini getirmek için bir kavanoz ve karanlıkta onları bağlar.
Marti Nito

Yanıtlar:


343

Kısacası Uber JAR, her şeyi içeren bir JAR'dır.

Normalde Maven'de bağımlılık yönetimine güveniyoruz. Bir eser yalnızca kendi sınıflarını / kaynaklarını içerir. Maven, projenin ne zaman inşa edildiğine bağlı olarak projenin tüm eserlerini (JAR'lar vb.) Bulmaktan sorumlu olacaktır.

Bir uber-jar, tüm bağımlılıkları alan ve bağımlılıkların içeriğini ayıklayan ve onları projenin sınıflarına / kaynaklarına tek bir JAR'da yerleştiren bir şeydir. Böyle bir uber-jar'a sahip olmak, yürütülmesi kolaydır, çünkü uygulamanızı çalıştırmak için ton küçük JAR yerine sadece bir büyük JAR'a ihtiyacınız olacaktır. Ayrıca bazı durumlarda dağıtımı da kolaylaştırır.

Sadece bir yan not. Maven'in bağımlılık çözümleme özelliğini bozduğu için uber-jar'ü Maven bağımlılığı olarak kullanmaktan kaçının. Normalde uber-jar'ü sadece gerçek dağıtım veya manuel dağıtım için son yapı için yaratırız, ancak Maven deposuna koymak için değil.


Güncelleme: Sorunun bir kısmını henüz yanıtlamadığımı keşfettim: "Bağımlılıkların paketlerini yeniden adlandırmanın anlamı nedir?". İşte bazı kısa güncellemeler ve umarım benzer soruları olan insanlara yardımcı olur.

Uygulama kolaylığı için uber-jar oluşturmak, gölge eklentisinin bir kullanım örneğidir. Paket yeniden adlandırmayı içeren başka yaygın kullanım durumları da vardır.

Örneğin, Foobelirli bir kitaplığın sürümüne (ör. 1.0) bağlı Barkitaplık geliştiriyorum. Diğer Barlib sürümlerini kullanamayacağımı varsayarsak (API değişikliği veya diğer teknik sorunlar vb. Nedeniyle). Ben sadece bildirirseniz Bar:1.0olarak FooMaven içinde bireyin bağımlılık, bir sorun düşmek mümkündür: Bir Quxproje bağlı olduğu Foove aynı zamanda Bar:2.0(ve kullanamazsınız Bar:1.0çünkü Quxihtiyaçlar yeni özelliği kullanmak için Bar:2.0). İşte ikilem: Quxkullanmalı Bar:1.0(hangisinin Quxkodu çalışmayacak) veya Bar:2.0(hangisinin Fookodu çalışmayacak)?

Bu sorunu çözmek için, geliştiricisi Fookendi kullanımını yeniden adlandırmak için gölge eklentisi kullanmayı tercih edebilirsiniz Bartüm sınıfları böylece, Bar:1.0kavanozda gömülü Fookavanoz ve gömülü paket Barsınıflardan değiştirilir com.bariçin com.foo.bar. Bunu yaparak, Quxgüvenli bir şekilde bağlı olabilir Bar:2.0çünkü şimdi Fooartık bağlı değildir Barve Barbaşka bir pakette bulunan "değiştirilmiş" kendi kopyasını kullanıyor .


5
Teşekkürler, bu çok yardımcı oluyor. Uber-jar içindeki bağımlılıkları ve diğer kavanozları gizlediği için buna "gölgeleme" denir mi?
12:28

6
Adın arkasındaki neden hakkında gerçekten emin değilim, ancak ön sayfasından, "gölgeleme" nin bağımlılıkların "gizlenmesini" açıkladığı anlaşılıyor. İsteğe bağlı olarak gölgeli JAR'a bazı bağımlılıklar ekleyebileceğiniz için, gölge eklentisi sizin için dahil olan bağımlılıkları kaldıran doğru bir POM üretecektir. Görünüşe göre gölgeleme bu süreci
anlatıyor

1
@AdrianShum: Gölge eklentisini kullanmadan uber kavanozu nasıl oluşturacağınızı önerebilir misiniz? Veya özellikle "Maven'in bağımlılık çözümleme özelliğini bozma" nasıl çözülür?
Suraj Menon

3
@SurajMenon: Shade eklentisini kullanmak, uber-jar oluşturmanın en kolay yoludur. Ancak, Montaj eklentisini ve yerleşik jar-with-dependencytanımlayıcıyı da kullanabilirsiniz . Uber-jar kullanarak bağımlılık roslution sorunu için, cevabımda daha önce bahsetmiştim: uber-jar bağımlılık olarak dönem kullanmayın. Biraz daha ayrıntılı: uber-jar oluşturmadan önce, normal bağımlılığa sahip normal bir projeniz olmalıdır. Bu orijinal eser bağımlılık olarak kullanmanız gereken şeydir (uber-jar yerine)
Adrian Shum

1
Sadece küçük bir soru: Class.forNameÇalışmayı durduruyor mu?
SOFe

64

Son zamanlarda, elasticsearch'ün bağımlılıklarının birkaçını (tümünü değil) gölgelediğini ve yerini değiştirdiğini merak ettim. İşte projenin sorumlusu @kimchy'den bir açıklama :

Gölgeleme kısmı kasıtlıdır, elasticsearch'te kullandığımız gölgeli kütüphaneler elasticsearch'ün tüm niyet ve amaçlı kısmı içindir, kullanılan versiyon, elasticsearch'in ortaya koyduğu ve kütüphanenin nasıl çalıştığına (ve kütüphanenin nasıl çalıştığına bağlı olarak kütüphaneyi nasıl kullandığına) bağlıdır. sürümler arasındaki değişiklikler), netty ve guava harika örneklerdir.

Btw, aslında biri lucene gölgeli ve diğeri Lucene gölgeli birkaç kavanoz elasticsearch sağlamakla sorunum yok. Maven ile nasıl yapılacağından emin değilim. Örneğin netti / jackson'u gölgelemeyen bir sürüm sağlamak istemiyorum, çünkü elasticsearch onlarla birlikte olan derin sindirici kullanım nedeniyle aslında daha az bellek kullanmaya kıyasla daha fazla bellek kullanır).

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

Ve burada drewr'dan bir tane daha :

Gölgeleme, bağımlılıklarımızı (özellikle netty, lucene, guava) kodumuza yakın tutmak için önemlidir, böylece yukarı akış sağlayıcısı geride kalsa bile bir sorunu düzeltebiliriz. Kodunuzun, özel sorununuza yardımcı olacak modülerleştirilmiş sürümlerini dağıtabiliriz (örneğin # 2091), ancak şu anda gölgeli bağımlılıkları kaldıramayız. Daha iyi bir çözüm olana kadar ES'nin yerel bir sürümünü amaçlarınız için oluşturabilirsiniz.

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

Yani, bu bir kullanım örneğidir. Açıklayıcı bir örneğe gelince, aşağıda maven-shade-plugin elasticsearch pom.xml'de (v0.90.5) nasıl kullanılır. artifactSet::includeUber JAR (temelde, bunlar fermuarını açıp ve hedef elasticsearch kavanoz üretildiği zaman elasticsearch kendi sınıflarının yanında yeniden paketlenmiş. (Zaten bunu bilmiyordum durumda içine çekmeye hangi bağımlılıklara bunun talimat çizgiler, bir JAR dosyası yalnızca programın sınıflarını, kaynaklarını vb. içeren bir ZIP dosyası ve bazı meta verileri içerir. Nasıl bir araya getirildiğini görmek için birini ayıklayabilirsiniz.)

relocations::relocationBu durumda, altına getirerek - satırları her durumda onlar da bağımlılık en sınıflara belirtilen değiştirmelerin geçerli olması dışında benzerdir org.elasticsearch.common.

Son olarak filtersbölüm, JAR meta verileri, karınca oluşturma dosyaları, metin dosyaları vb. Gibi, orada bulunmaması gereken hedef JAR'dan bazı bağımlılıklar ile paketlenmiş ancak uber JAR'a ait olmayan bazı şeyleri hariç tutar.

<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

Küçük uyarı

Bu neden maven-gölge eklentisini kullanmak istediğini açıklamasa da (seçilen cevap oldukça iyi tanımladığından), sorun yaşadığımı belirtmek isterim. JAR'ı değiştirdi (çünkü yaptığı şey) ve yazılımımda gerilemeye neden oldu.

Yani, bunu (veya maven-jarjar-eklentisini) kullanmak yerine, problemsiz çalışıyor gibi görünen JarJar'ın ikili dosyasını kullandım.

Çözümümü buraya gönderiyorum çünkü iyi bir çözüm bulmam biraz zaman aldı.


Downlaod JarJar'ın JAR dosyası

Kavanozu buradan indirebilirsiniz: https://code.google.com/p/jarjar/ Soldaki menüde indirmek için bir bağlantınız var.


Bir JAR sınıfını bir paketten diğerine taşımak için JarJar nasıl kullanılır

Bu örnekte paketi "com.fasterxml.jackson" yerine "io.kuku.dependencies.com.fasterxml.jackson" olarak değiştireceğiz. - JAR kaynağına "jackson-databind-2.6.4.jar" ve yeni değiştirilmiş (hedef) JAR'a "kuku-jackson-databind-2.6.4.jar" adı verilir. - "Jarjar" JAR dosyası 1.4 sürümündedir

  1. Bir "rules.txt" dosyası oluşturun. Dosyanın içeriği ('@' karakterinden önceki dönemi izleyin): kural com.fasterxml.jackson. ** io.kuku.dependencies.com.fasterxml.jackson. @ 1

  2. Aşağıdaki komutu çalıştırın: java -jar jarjar-1.4.jar process rules.txt jackson-databind-2.6.4.jar kuku-jackson-databind-2.6.4.jar


Değiştirilen JAR'ları yerel depoya yükleme

Bu durumda "c: \ my-jars \" klasöründe bulunan 3 dosyayı yüklüyorum.

mvn install: install-file -Dfile = C: \ benim kavanozlar \ kuku-jackson-ek açıklamalar-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-ek açıklamalar -Dersiyon = 2.6.4 - Dpackaging = kavanoz

mvn install: install-file -Dfile = C: \ benim kavanozlar \ kuku-jackson-core-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-core -Dversion = 2.6.4 - Dpackaging = kavanoz

mvn install: install-file -Dfile = C: \ benim-kavanozlar \ kuku-jackson-databind-2.6.4.jar -DgroupId = io.kuku.dependencies -DartifactId = kuku-jackson-ek açıklamalar -Dersiyon = 2.6.4 - Dpackaging = kavanoz


Projenin pom'unda değiştirilmiş JAR'ları kullanma

Bu örnekte, bu pom projelerindeki "bağımlılıklar" öğesidir:

<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
Bu alternatif öneri için teşekkürler. Aradığım şey bu değildi, ama asla değişmeyecek eski kütüphanelere 1 seferlik paket çevirileri uygulamak için çok daha basit ve daha hızlı bir çözüm olduğu ortaya çıktı.
Kahrolası

İlgili çıktılarının içeriğini karşılaştırarak bu tür başarısızlıkların nedenini anladınız mı?
tribbloid

2

"Gölgeli" bir kavanoz ihtiyacının bir örneği AWS Lambda fonksiyonudur. Tipik bir .war dosyasında bulacağınız gibi .jar'ların tüm koleksiyonunu değil, yalnızca 1 kavanoz yüklemenize izin veriyor gibi görünüyorlar. Böylece, projenin tüm bağımlılıklarını içeren tek bir .jar oluşturmak bunu yapmanızı sağlar.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.