top bar

글 목록

2016년 2월 28일 일요일

[MAVEN] Shade Plugin (2) - artifactSet과 filter 설정 사용 및 minimizeJar

들어가기 앞서



가령 아래와 같은 구조의 project-a, project-b, project-c 라고 하는
3개의 프로젝트가 있다고 하자. (클릭하면 크게 보임)





이때 'MainClass' 가 있는 'project-a'의 pom.xml의 'dependency' 설정은 아래와 같이 구성한다.
<dependencies>

    <dependency>
        <groupId>com.asuraiv.bbb</groupId>
        <artifactId>project-b</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
    </dependency>

</dependencies>

간단히 코드도 살펴보자.

'project-a'의 'MainClass'는 아래처럼 'project-b'의
'com.asuraiv.bbb.util_1.B_Utils1' 클래스를 사용한다.
package com.asuraiv.aaa;

import com.asuraiv.bbb.util_1.B_Utils1;

public class MainClass {
    
    public static void main(String[] args) {
        
        System.out.println("Hello Shade Plugin !!");
        
        // project-b 의 Utils 클래스 사용
        B_Utils1.printString("Hello Project B");
    }
}
'B_Utils1.printString' 메소드는 아래처럼 단순히 System out으로 문자열을 찍는다.
package com.asuraiv.bbb.util_1;

public class B_Utils1 {
    
    public static void printString(String text) {
        System.out.println(text);
    }
}

한편, 'com.asuraiv.bbb.util_2.B_Utils2' 클래스는 'project-c' 의 클래스를 사용한다.
package com.asuraiv.bbb.util_2;

import com.asuraiv.ccc.util_2.C_Utils2;

public class B_Utils2 {
    
    public static void printIntegerUsingCUtils(int value) {
        
        // project-c 의 Utils 클래스 사용
        C_Utils2.printInteger(value);
    }
}
뭐, 이런 상황이다.

이렇게 되면 이들 3개의 프로젝트의 의존관계는 아래처럼 될 것이다.










정리하자면 'project-a' 의 'MainClass' 에서 'project-b'의 'com.asuraiv.bbb.util_1.B_Utils1' 를 사용하고, 'project-b' 의 다른 패키지인 'com.asuraiv.bbb.util_2.B_Utils2'에서는 'project-c'의 'com.asuraiv.ccc.util_2.C_Utils2' 를 사용하는 것이다.

이때 'project-a'를 shade 플러그인을 사용하여 'uber-jar' 만들고,
디렉토리 구조를 보면 아래와 같은 것이다.
├─com
│  └─asuraiv
│      ├─aaa
│      ├─bbb
│      │  ├─util_1
│      │  └─util_2
│      └─ccc
│          ├─util_1
│          └─util_2
├─junit
│  ├─awtui
│  ├─extensions
│  ├─framework
│  ├─runner
│  ├─swingui
│  │  └─icons
│  └─textui
└─META-INF
    └─maven
        ├─com.asuraiv.aaa
        │  └─project-a
        ├─com.asuraiv.bbb
        │  └─project-b
        └─com.asuraiv.ccc
            └─project-c


artifactSet



위의 상황들이 전제로 주어졌을때, 결과적으로 'project-c'는 'project-b'가 의존하고 있지만, 사용되지 않는다. 'project-a'의 'MainClass'가 'project-b'의 'com.asuraiv.bbb.util_1.B_Utils1' 만을 사용하고 있기 때문이다.

하지만 project-c를 사용하지 않음에도 불구하고 shade 플러그인을 사용해 생성된 uber-jar는 위의 tree 구조를 보면 알 수 있듯 물리고 물린 의존관계는 몽땅 패키징이 되어 사용되지 않더라도 uber-jar 안에 포함되 있는것을 볼 수 있다.

이때 사용하지 않는 'proejct-c' 의 의존관계를 uber-jar 생성시에 제외시킬 수 있다.

'configuration' 태그 밑의 'artifactSet' 설정을 사용하여 어떤 라이브러리를 제외하고 포함시킬 것인지 정의가 가능하다. 아래와 같이 설정한다.
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.4.3</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.asuraiv.aaa.MainClass</mainClass>
                    </transformer>
                </transformers>
                <artifactSet>
                    <excludes>
                        <exclude>com.asuraiv.ccc:project-c</exclude>
                    </excludes>                             
                </artifactSet>
                <outputFile>d:/shade/project-a.jar</outputFile>
            </configuration>
        </execution>
    </executions>
</plugin>
'artifactSet'태그와 함께 'excludes' 설정을 하면, 어떤 라이브러리를 제외 시킬 것인지 명시 할 수 있게 된다. 더불어 'outputFile'은 jar 파일이 떨어지는 위치를 말한다.

이러한 설정을 바탕으로 package를 하게 되면 아래와 같은 tree 구조가 된다.
├─com
│  └─asuraiv
│      ├─aaa
│      └─bbb
│          ├─util_1
│          └─util_2
├─junit
│  ├─awtui
│  ├─extensions
│  ├─framework
│  ├─runner
│  ├─swingui
│  │  └─icons
│  └─textui
└─META-INF
    └─maven
        ├─com.asuraiv.aaa
        │  └─project-a
        └─com.asuraiv.bbb
            └─project-b
보는것과 같이 사용하지 않는 'project-c' 가 패키징 되지 않았다.


filters



하지만 아래처럼 'MainClass'에서 'B_Utils1.printString'을 사용하지 않고 'project-c'를 사용하여 문자열을 찍는 'B_Utils2.printIntegerUsingCUtils' 메소드를 사용한다면 어떨까?
package com.asuraiv.aaa;

import com.asuraiv.bbb.util_2.B_Utils2;

public class MainClass {
    
    public static void main(String[] args) {
        
        System.out.println("Hello Shade Plugin !!");
        
        // project-c를 사용하여 문자열을 찍는 project-b의 Utils 클래스를 사용
        B_Utils2.printStringUsingCUtils("Hello Project B");
    }
}
'project-c' 라이브러리를 통째로 날렸다가는 프로그램 실행시에 아래와 같은 에러를 볼 것이다.



'MainClass'에서, 'project-c' 라이브러리를 사용하는 'project-b'의 메소드를 호출했는데, 'project-c'가 통째로 제외 되었으니 런타임에 오류가 나는것은 당연하다.

이때 'filter' 설정을 사용해 'project-c' 라이브러리에서 사용하는 패키지를 남기고 나머진 제외 시켜서 uber-jar를 생성할 수 있다!
<configuration>
    <transformers>
        <transformer
            implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>com.asuraiv.aaa.MainClass</mainClass>
        </transformer>
    </transformers>
    <artifactSet>
        <excludes>
            <exclude>junit:junit</exclude>
        </excludes>
    </artifactSet>
    <filters>
        <filter>
            <artifact>com.asuraiv.ccc:project-c</artifact>
            <excludes>
                <exclude>com/asuraiv/ccc/util_2/**</exclude>
            </excludes>
        </filter>
    </filters>
    <outputFile>d:/project-a.jar</outputFile>
</configuration>
앞서 배운 'artifactSet' 설정을 사용하여 'junit'을 통째로 제외 시켰다.

여기서 'filters' 설정에 주목하자. 현재 'project-b'의 'printStringUsingCUtils' 메소드는, 'project-c'의 'com.asuraiv.ccc.util_1' 패키지만을 사용하고 'com.asuraiv.ccc.util_2' 패키지는 사용하지 않는다. 따라서 'filters' 설정을 사용하여, 특정 라이브러리의 특정 패키지만을 제외 시킨 것이다.

tree로 디렉토리 구조를 보면 아래와 같이 필요한 패키지만 uber-jar에 포함되어 있다.
├─com
│  └─asuraiv
│      ├─aaa
│      ├─bbb
│      │  ├─util_1
│      │  └─util_2
│      └─ccc
│          └─util_1
└─META-INF
    └─maven
        ├─com.asuraiv.aaa
        │  └─project-a
        ├─com.asuraiv.bbb
        │  └─project-b
        └─com.asuraiv.ccc
            └─project-c
ccc.utils_2 패키지가 제외 되었다. 근데 사실 bbb.utils_1 패키지도 사용하지 않는다.
filter 설정을 추가해보자
<filters>
    <filter>
        <artifact>com.asuraiv.bbb:project-b</artifact>
        <excludes>
            <exclude>com/asuraiv/bbb/util_1/**</exclude>
        </excludes>
    </filter>
    <filter>
        <artifact>com.asuraiv.ccc:project-c</artifact>
        <excludes>
            <exclude>com/asuraiv/ccc/util_2/**</exclude>
        </excludes>
    </filter>                               
</filters>
위와같이 'project-b' 에 관련한 필터설정도 추가했다.

빌드후에 디렉토리 구조를 보면 아래와 같이 필요없는 패키지들이 모두
제거된 것을 볼 수 있다.
├─com
│  └─asuraiv
│      ├─aaa
│      ├─bbb
│      │  └─util_2
│      └─ccc
│          └─util_1
└─META-INF
    └─maven
        ├─com.asuraiv.aaa
        │  └─project-a
        ├─com.asuraiv.bbb
        │  └─project-b
        └─com.asuraiv.ccc
            └─project-c


minimizeJar



사실 'artifactSet' 이나 'filter' 설정처럼 세밀하게 포함될/제외될 라이브러리나 패키지를 설정하기 힘들다면, 'minimizeJar' 설정으로 간편하게 필요없는 소스코드들을 제외 시킬 수 있다
<configuration>
    <transformers>
        <transformer
            implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>com.asuraiv.aaa.MainClass</mainClass>
        </transformer>
    </transformers>
    <minimizeJar>true</minimizeJar> <!-- 필요없는 소스코드 제외! -->
    <outputFile>d:/shade/project-a.jar</outputFile>
</configuration>
위와 같이 'minimizeJar' 태그 설정만 하면 우리 위에서 살펴봤던 'artifactSet'과 'filter'설정을 한 것과 동일한 결과를 가져온다.

여태까지 'exclude'의 경우만 설명하고 'include'의 경우를 설명하지 않았는데, 사용법은 'exclude'와 동일하다.

만약 minimizeJar를 사용했는데 런타임에 필요한 패키지까지 제외되어 오류가 난다면 'include' 설정을 사용하여 필요한 패키지를 포함 시킬 수 있을 것이다.

'artifactSet'을 사용하여 어떤 라이브러리를 포함/제외 시킬 것인지, 'filter'를 사용하여 패키지 레벨로 세세하게 명시할 것인지, minimizeJar를 사용하여 간편하게 jar파일을 만들것인지.. 이 모든 방법들을 적당하게 조합하여 사용하면 효율적인 용량의 jar 파일을 만들 수 있을 것이다.


댓글 없음:

댓글 쓰기