建立可執行檔

我們首先要瞭解,為什麼我們可以直接執行某些 jar 檔? 像 java -jar h2-2.2.224.jar

原因很簡單,jar 本身就是一個 zip 壓縮檔,本身包含一份艙單在 META-INF/MANIFEST.MF。

java -jar執行時,java會先開啟這份艙單,讀取相關的資訊,包可執行那個類別或外部應有相依的Library。

然後就像正常的執行 class 一樣開始執行這個程式。

所以當我們打包這個 jar 檔時,就必須在相應的外掛中,設定相關的資料。

  <properties>
    <exec.mainClass>完整類別名稱(有main方法的class)</exec.mainClass>
  <properties>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <version>3.3.0</version>
        <configuration>
          <archive>
            <manifest>
              <addClasspath>true</addClasspath>
              <mainClass>${exec.mainClass}</mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>
    </plugins>
  </build>

上述的作用僅僅是把預設執行的Class寫入艙單內,若是還需要外部的Jar協同執行的話,您就必須凖備好相應的Jar檔,並把這些jars檔加入Classpath中(也可以相對的徑,寫入艙單內,然後自動加入ClassPath),不過一般除了JDBC的Jar外不太這樣做,因為我們不知道這些相關的Jars檔是否會迭失,或是User自行換了更新版本的Jar導致程式不相容的情況發生,所以一般可以把這些要用到的Jars檔,把所有的classes一起打成一包,做法改用Pluing如下:

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.6.0</version>
    <executions>
      <execution>
        <id>make-assembly</id>
        <phase>package</phase>
        <goals>
          <goal>single</goal>
        </goals>
      </execution>
    </executions>
    <configuration>
      <archive>
        <manifest>
          <mainClass>${exec.mainClass}</mainClass>
        </manifest>
      </archive>
      <descriptorRefs>
        <descriptorRef>jar-with-dependencies</descriptorRef>
      </descriptorRefs>
    </configuration>
  </plugin>

剛剛我為什麼提JDBC的Jar通常不會用這種方式包進來?建議您找個JDBC的Jar解Zip出來看看,其實META-INF/內包含的不只是艙單,有時會有像services/這樣的目錄用來記錄service provider的Class有那些,更甚者,像spring framework還有自訂的目錄結構,而如何處理這樣複雜的結構就必須要使用不同的外掛與處理。

此時我們需要改用maven-shade-plugin來建立這個Jar檔。

下面是針對使用 spring 與 Log4j2的設定範例。

  <properties>
    <exec.mainClass>完整類別名稱(有main方法的class)</exec.mainClass>
  <properties>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.5.1</version>
        <dependencies>
          <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-transform-maven-shade-plugin-extensions</artifactId>
            <version>0.1.0</version>
          </dependency>
        </dependencies>
        <executions>
          <execution>
            <phase>package</phase><!--生命週期章節再解釋-->
            <goals><goal>shade</goal></goals>
            <configuration>
              <artifactSet>
                 <excludes><!--打包時排除那些您認為多餘的Library -->
                    <exclude>org.slf4j:slf4j-api:jar:</exclude><!--最後省略的是版本號-->
                    <exclude>com.microsoft.sqlserver:mssql-jdbc:jar:</exclude>
                 </excludes>
              </artifactSet>
              <transformers><!--指定那些可以處理打包的類別,各自進來處理打包-->
                <transformer implementation="org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer"/>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <mainClass>${exec.mainClass}</mainClass>
                  <manifestEntries><!--jar檔是否包含不同JDK版本的class-->
                    <Multi-Release>false</Multi-Release>
                  </manifestEntries>
                 </transformer>
              </transformers>
              <filters>
                <filter>
                  <artifact>*:*</artifact>
                  <excludes><!--打包時排除索引與簽章檔-->
                    <exclude>META-INF/*.SF</exclude>
                    <exclude>META-INF/*.DSA</exclude>
                    <exclude>META-INF/*.RSA</exclude>
                  </excludes>
                </filter>
              </filters>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

其中<artifactSet>是用來排除不想被包進來的的Jar檔,<transformers>則是指指定讓哪些外部程式進來處理META-INF/,此例需要外部class進來處理Log4j的打包,所以引用了要第三方dependency,要如何建立可執行檔,請參考官方文檔,也建議看一下Resource Transformers章節,裡面就有包含怎麼打包Spring程式的說明。