maven-shade-plugin은 무엇이며 Java 패키지를 재배치하려는 이유는 무엇입니까?


282

누군가의 pom.xml에서 maven-shade-plugin이 사용되고 있음을 발견했습니다. 나는 전에 maven-shade-plugin을 사용한 적이 없으며 (Maven n00b입니다) 이것을 사용하는 이유와 그것이하는 일을 이해하려고했습니다.

나는 Maven docs를 보았지만 이 진술을 이해할 수 없다.

"이 플러그인은 아티팩트를 포함하여 아티팩트를 아군에 패키지하고 일부 종속성의 패키지를 음영 처리 (즉, 이름 바꾸기)하는 기능을 제공합니다."

페이지의 문서는 초보자에게 친숙하지 않습니다.

"우버 병"이란 무엇입니까? 누군가가 왜 하나를 만들고 싶습니까? 의존성 패키지의 이름을 바꾸는 요점은 무엇입니까? "Uber Jar의 컨텐츠 선택"과 같은 maven-shade-plugin 아파치 페이지의 예제를 살펴 보려고했지만 여전히 "shading"으로 수행중인 작업을 이해할 수 없습니다.

예시적인 예 / 사용 사례 (이 경우 음영이 필요한 이유에 대한 설명과 함께 어떤 문제가 해결되는지에 대한 설명)에 대한 모든 의견을 주시면 감사하겠습니다. 마지막으로 maven-shade-plugin을 언제 사용해야합니까?


16
"uber jar"라는 이름의 경우 "über"는 "over"를 의미하는 독일어와 유일하게 연관되어 있습니다. 문자 그대로 "jar-over-all-other-jars"를 의미합니다. "셰이딩"은 충돌하는 클래스 또는 리소스에 필요한 "패키지 재 할당"과 동일합니다.
dma_k 2013

1
s/reallocation/relocation/위의 의견에서.
Christopher Schultz

12
동네 짱 항아리는 하나의 고리와 같습니다 : 하나의 항아리가 그들을 모두 지배하고, 하나의 항아리를 찾아서, 하나의 항아리를 가져 와서 어둠 속에서 그들을 묶습니다.
Marti Nito

답변:


343

간단히 말해 Uber JAR은 모든 것을 포함하는 JAR입니다.

일반적으로 Maven에서는 종속성 관리에 의존합니다. 이슈에는 자체 클래스 / 리소스 만 포함됩니다. Maven은 프로젝트 빌드시기에 따라 프로젝트에있는 모든 아티팩트 (JAR 등)를 찾아야합니다.

동네 짱 항아리는 모든 종속성을 가져 와서 종속성의 내용을 추출하여 프로젝트 자체의 클래스 / 리소스와 함께 하나의 큰 JAR에 넣는 것입니다. 이러한 uber-jar을 사용하면 앱을 실행하기 위해 수많은 작은 JAR 대신 하나의 큰 JAR 만 필요하기 때문에 실행하기 쉽습니다. 경우에 따라 배포가 용이합니다.

단지 참고 사항입니다. Maven의 종속성 해결 기능을 망치므로 uber-jar을 Maven 종속성으로 사용하지 마십시오. 일반적으로 실제 배포 또는 수동 배포를위한 최종 아티팩트에 대해서만 uber-jar을 작성하지만 Maven 저장소에는 넣지 않습니다.


업데이트 : 방금 질문의 한 부분에 대답하지 않았다는 것을 발견했습니다. "종속성의 패키지 이름을 바꾸는 요점은 무엇입니까?" 다음은 간단한 업데이트이며 유사한 질문을 가진 사람들을 도울 것입니다.

쉬운 배포를위한 uber-jar 작성은 쉐도우 플러그인의 사용 사례입니다. 패키지 이름 바꾸기와 관련된 다른 일반적인 사용 사례도 있습니다.

예를 들어, Foo라이브러리의 특정 버전 (예 : 1.0)에 따라 라이브러리를 개발 중 Bar입니다. BarAPI 변경 또는 기타 기술적 문제 등으로 인해 다른 버전의 lib를 사용할 수 없다고 가정 합니다. 나는 단순히 선언하는 경우 Bar:1.0Foo메이븐에의 의존성,이 문제로 분류 할 수 있습니다 :의 Qux프로젝트에 따라되고 Foo, 또한 Bar:2.0(그리고 사용할 수 Bar:1.0있기 때문에 Qux필요에 새로운 기능을 사용 Bar:2.0). 여기에 딜레마는 다음과 같습니다해야 Qux사용 Bar:1.0(이 Qux의 코드는하지 않습니다 작업) 또는 Bar:2.0( Foo의 코드가 아닙니다 된 작업이)?

이 문제를 해결하기 위해 개발자 개발자는 Foo음영 플러그인을 사용하여 사용법의 이름을 바꾸어 jar의 Bar모든 클래스가 Bar:1.0jar에 포함되고 Foo포함 된 Bar클래스 의 패키지 가에서 com.bar로 변경 되도록 선택할 수 있습니다 com.foo.bar. 이렇게하면 더 이상에 의존하지 않기 때문에 Qux안전하게 의존 할 수 있으며 다른 패키지에있는 "변경된"사본을 사용하고 있습니다.Bar:2.0FooBarBar


5
고마워, 그것은 많은 도움이됩니다. uber-jar 내부의 종속성과 다른 항아리를 숨기므로 "shading"이라고합니까?
nonbeing

6
나는 이름의 이유가 무엇인지 확실하지 않지만 첫 페이지에서 "음영"이 종속성의 "숨김"을 설명하고 있다고 생각합니다. 선택적으로 음영 처리 된 JAR에 일부 종속성을 포함 할 수 있으므로 음영 플러그인은 포함 된 종속성을 제거하는 올바른 POM을 생성합니다. 음영이 과정을 설명하는 것 같다
아드리안 셤에게

1
@AdrianShum : shade 플러그인을 사용하지 않고 uber jar를 만드는 방법을 제안 할 수 있습니까? 또는 구체적으로 "Maven의 종속성 해결 기능의 파괴"를 해결하는 방법은 무엇입니까?
Suraj Menon

3
@SurajMenon : Shade 플러그인을 사용하는 것이 가장 쉬운 방법입니다. 그러나 어셈블리 플러그인을 사용하고 내장 jar-with-dependency디스크립터를 사용할 수도 있습니다 . uber-jar을 사용한 의존성 roslution의 문제에 대해서는 이미 내 대답에서 언급했습니다 : uber-jar을 의존성, 기간으로 사용하지 마십시오. 좀 더 자세하게 : uber-jar을 작성하기 전에 정상적인 종속성을 가진 정상적인 프로젝트가 있어야합니다. 그 원래 아티팩트는 (uber-jar 대신) 의존성으로 사용해야하는 것입니다
Adrian Shum

1
사소한 질문 : Class.forName작동 이 중지됩니까 ?
SOFe

64

나는 최근에 elasticsearch가 왜 (그리고 전부는 아니지만) 그 의존성을 옮기고 있는지 궁금해하고있었습니다. 다음은 프로젝트 관리자 인 @kimchy 의 설명입니다 .

음영 처리 부분은 의도적이며, elasticsearch에서 사용하는 음영 처리 된 라이브러리는 elasticsearch의 모든 의도 및 목적 부분에 사용되며, 사용되는 버전은 elasticsearch가 노출하는 내용 및 라이브러리 작동 방식 (및 내부 작동 방식)을 기반으로 라이브러리를 사용하는 방법에 밀접하게 연결되어 있습니다. 네티와 ​​구아바는 버전 간 변경이 좋은 예입니다.

Btw, 나는 실제로 여러 개의 탄성 검색 병을 제공하는 데 아무런 문제가 없습니다. 하나는 lucene이 음영 처리되지 않은 것이고 다른 하나는 Lucene이 음영 처리되어 있습니다. 그래도 maven으로 어떻게하는지 모르겠습니다. 예를 들어, Elasticsearch가 가지고있는 긴밀한 사용법으로 인해 예를 들어 netty / jackson을 가리지 않는 버전을 제공하고 싶지 않습니다 (예 : 현재 버전을 제외한 이전 버전의 netty에서 곧 버퍼링 개선 사용) 실제로 훨씬 적은 메모리를 사용하는 것과 비교하여 더 많은 메모리를 사용합니다).

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

그리고 drawr의 또 다른 여기 :

쉐이딩은 의존성 (특히 netty, lucene, guava)을 코드와 가깝게 유지하여 업스트림 공급자가 뒤쳐져도 문제를 해결할 수 있도록하는 데 중요합니다. 특정 문제에 도움이되는 모듈화 된 코드 버전을 배포 할 수 있지만 (예 : # 2091) 현재 음영 처리 된 종속성을 제거 할 수는 없습니다. 더 나은 솔루션이 될 때까지 목적에 맞게 로컬 ES 버전을 구축 할 수 있습니다.

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

이것이 하나의 유스 케이스입니다. 예시적인 예를 들어, 다음은 elasticsearch의 pom.xml (v0.90.5)에서 maven-shade-plugin이 사용되는 방법입니다. 이 artifactSet::include줄은 uber JAR로 가져올 종속성을 지시합니다 (기본적으로 대상 elasticsearch jar이 생성 될 때 elasticsearch의 자체 클래스와 함께 압축이 풀리고 다시 패키지됩니다 (이미 알 수없는 경우 JAR 파일은 프로그램의 클래스, 리소스 등이 포함 된 ZIP 파일과 일부 메타 데이터가 포함되어 있습니다. 파일을 추출하여 구성 방법을 확인할 수 있습니다.)

relocations::relocation이 경우, 아래를 데려 - 라인은 각각의 경우에 그들은 또한 의존성의 클래스에 지정된 대체를 적용하는 것을 제외하고 유사하다 org.elasticsearch.common.

마지막 filters으로이 섹션에서는 대상 메타 데이터, 개미 빌드 파일, 텍스트 파일 등과 같이 종속 항목으로 패키지화되었지만 uber JAR에는 속하지 않는 대상 JAR에서 일부 항목을 제외합니다.

<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

작은 경고

비록 선택한 답변이 꽤 잘 설명되어 있기 때문에 maven-shade-plugin을 사용하려는 이유를 설명하지는 않지만 문제가 있음을 알고 싶습니다. JAR이 변경되었으므로 (내가하는 일 때문에) 내 소프트웨어에서 회귀를 일으켰습니다.

따라서이 (또는 maven-jarjar-plugin)를 사용하는 대신 문제없이 작동하는 JarJar의 이진 파일을 사용했습니다.

적절한 솔루션을 찾는 데 시간이 걸리기 때문에 여기에 솔루션을 게시하고 있습니다.


Downlaod JarJar의 JAR 파일

https://code.google.com/p/jarjar/ 에서 항아리를 다운로드 할 수 있습니다 . 왼쪽 메뉴에 다운로드 할 수있는 링크가 있습니다.


한 패키지에서 다른 패키지로 JAR 클래스를 재배치하기 위해 JarJar를 사용하는 방법

이 예에서는 패키지를 "com.fasterxml.jackson"에서 "io.kuku.dependencies.com.fasterxml.jackson"로 변경합니다. -소스 JAR을 "jackson-databind-2.6.4.jar"이라고하고 새로 수정 된 (대상) JAR을 "kuku-jackson-databind-2.6.4.jar"이라고합니다. - "jarjar"JAR 파일은 1.4 버전입니다.

  1. "rules.txt"파일을 작성하십시오. 파일의 내용은 다음과 같아야합니다 ( '@'문자 앞의 기간을보십시오) : rule com.fasterxml.jackson. ** io.kuku.dependencies.com.fasterxml.jackson. @ 1

  2. 다음 명령을 실행하십시오. java -jar jarjar-1.4.jar process rules.txt jackson-databind-2.6.4.jar kuku-jackson-databind-2.6.4.jar


로컬 저장소에 수정 된 JAR 설치

이 경우 "c : \ my-jars \"폴더에 3 개의 파일을 설치하고 있습니다.

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 = jar

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 = jar

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 = jar


프로젝트의 pom에서 수정 된 JAR 사용

이 예에서 프로젝트 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
이 대체 제안에 감사드립니다. 이것은 내가 찾던 것이 아니었지만 결코 변하지 않을 레거시 라이브러리에 1 회 패키지 번역을 적용하기위한 훨씬 간단하고 빠른 솔루션으로 판명되었습니다.
Frelling

각각의 출력 내용을 비교하여 이러한 실패의 원인을 파악 했습니까?
tribbloid

2

"음영 처리 된"jar의 필요성에 대한 한 가지 예는 AWS Lambda 함수라고 생각합니다. 그들은 당신이 전형적인 .war 파일에서 찾을 수있는 것처럼 .jars의 전체 컬렉션이 아닌 1 개의 jar 파일 만 업로드 할 수있는 것처럼 보입니다. 따라서 모든 프로젝트 종속성으로 단일 .jar을 작성하면이 작업을 수행 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.