개요
메이븐에서 자바로 구성된 프로젝트를 자바 아카이브 파일 (이하 jar 파일)로 말아(?) 주는 플러그인이 몇가지가 있는데, 오늘 정리할 플러그인은 'maven-shade-plugin' 이다.
일단 정리하기에 앞서 'uber-jar' 라는 개념에 대해서 살펴볼 필요가 있다.
아래 페이지에 전형적인 질/답 이 나와 있다.
http://stackoverflow.com/questions/11947037/what-is-an-uber-jar
'uber' 라는 말은 독일어로서, 'above' 또는 'over' 라는 뜻이 있다고 한다.
그러니까 영어로 따지면 'over-jar' 인 것이다.
각설하고, 'uber-jar'라는 것은 자바 어플리케이션의 모든 패키지와, 그에
의존관계에 있는 패키지 라이브러리까지
모두 하나의 'jar' 에 담겨져 있는 것 을 말한다.
'uber-jar'를 사용하면 어플리케이션을 배포할때 의존관계를 생각할 필요가 없다. 왜냐면 이미 필요한 의존관계 라이브러리들을 가지고 있기 때문이다.
Maven shade plugin
이러한 'uber-jar'를 생성할때, 해당 어플리케이션에 모든 의존관계까지 포함하다 보니, 어플리케이션 배포시에 필요없는 라이브러리까지(예를들어서 개발 프로세스에만 필요한 라이브러리 - JUnit같은..? - 라던가..) 몽땅 패키징되는 경우가 있다.
shade 플러그인의 강력함은, 배포시에 필요한 라이브러리들을 'exclude/include' 시킬 수있고, 라이브러리 수준뿐만 아니라 class 파일 수준으로 jar 파일을 minimize 함으로서 보다 가벼운 jar 파일을 생성할 수 있다는 것이다. (그러니까 미사용 class 파일도 걸러낸다는 말이다.)
기본 사용법
아래는 기본적인 사용법이다.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<configuration>
<!-- put your configurations here -->
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
메이븐 골(goal)로 'shade:shade' 를 입력하여 직접 구동 시킬 수 있지만, <executions> 설정으로 package phase에 shade 골을 바인딩 하는 설정을 하면, 'mvn package' 으로 구동 시킬 수 있다.
Resource Transformer
shade 플러그인을 적용 할때 'Resource Transformer' 라는 개념을 이해할 필요가 있다
Resource Transformer 설정을 하면 서로 다른 artifacts 들로부터 uber-jar 를 생성할때, classes 및 resources 파일들을 '중복없이' 패키징 할 수 있게 해준다.
각 Resources Transformer 설정의 종류 및 특징은 아래와 같다
이들 설정 가운데 흔히 쓰이는 몇가지 설정만 살펴보자.
(1) ManifestResourcesTransformer
여기서 주로 쓰이는 것은 ManifestResourcesTransformer 인데, 설명에 나와있는대로 자바 'MANIFEST' 파일의 entries 를 세팅해 준다.
아래와 같이 <configuration> 설정에 추가한다
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.asuraiv.project.aaa.MainClass</mainClass>
</transformer>
</transformers>
</configuration>
실행 가능한 jar 파일을 생성할시에 자바 어플리케이션을 구동할 MainClass를 지정해야하는데, 이것은 'MANIFEST' 파일의 entry 중 하나이다. 'MANIFEST' 파일 관련은
이전 포스트를 참고하길 바란다.
위 예제처럼 <mainClass> 설정으로 해당 어플리케이션의 메인클래스를 입력한다.
(2) AppendingTransformer
만약 스프링 batch 프로젝트를 shade 플러그인을 통해 'Executable JAR' 파일로 패키징 한다고 하자. 그럴 경우 Main 클래스는 스프링 batch job을 커맨드 라인에서 실행 할 수 있게 해주는 'org.springframework.batch.core.launch.support.CommandLineJobRunner' 가 된다.
그러면 ManifestResourceTransformer의 <mainClass> 설정만 해당 클래스로 설정해주면 될까?
그것만 해서는 안된다.
스프링으로 구성된 어플리케이션은 스프링 컨텍스트 xml의 namespace를 핸들링 해주는 Handler 클래스들이 정의되어 있는 spring.handlers 파일과, 스프링 컨텍스트 xml 설정 파일의 스키마(xsd 파일 등)가 정의되어 있는 spring.schemas 파일이 필요하다.
바로 이때, AppendingTransformer 설정을 사용하여 uber-jar에 포함 시킬 수 있다.
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>org.springframework.batch.core.launch.support.CommandLineJobRunner</mainClass>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
</configuration>
기본적으로 스프링 라이브러리 jar 파일(spring-context, spring-aop, spring-beans 등등...)을 까보면 각각 META-INF 밑에 spring.handlers, spring.schemas 파일이 존재한다.
앞서 설명했듯이 shade 플러그인이 이 모든 의존관계 라이브러리들을 한데 묶어서 uber-jar 를 생성할때, 위 2개의 파일들이 각각 스프링 라이브러리에
동일한 이름으로 존재(하지만 그 내용은 또 각각 다르다ㅠㅠ)하기 때문에 중복의 문제가 존재한다.
따라서 AppendingTransformer 설정으로 해당 파일들을 포함시키면, 마치 'merge' 를 하는것과 같이 각 라이브러리의 핸들러, 스키마 정보들이
각각 하나의 spring.handlers, spring.shcemas 파일로 생성되는 것이다. 신통하다!
ResourcesTransformer에 관해 여기서 다 정리할 수는 없으니, 나머지는 공식 도큐먼트를 참고하자
다음 포스트에는 앞서 설명한 artifacts 단위로 include/exclude 하는 방법, minimize 설정을 통해 가벼운 jar 파일을 생성하는 방법 등을 정리해야 것다!