建立獨立的Java可執行JAR的三種方法 - frankel
在這篇文章中,我們描述了三種建立獨立的可執行JAR的方法。
當您的應用程式超出了十幾行程式碼時,您可能應該將程式碼分成多個類。在Java中,經典打包格式是Java ARchive,也稱為JAR。但是實際應用程式可能依賴於其他JAR包。
這篇文章旨在描述建立獨立的可執行JAR(也稱為uber-JAR或胖JAR)的方法。
什麼是可執行JAR?
JAR只是類檔案的集合。為了可執行,其META-INF/MANIFEST.MF檔案應指向實現該main()方法的類。您可以使用Main-Class屬性來執行此操作。這是一個例子:
Main-Class: path.to.MainClass
MainClass有一個static main(String… args)方法 |
處理類路徑
大多數應用程式依賴現有程式碼。Java提供了類路徑classpath的概念。類路徑是執行時將查詢以查詢依賴程式碼的路徑元素的列表。當執行Java類,定義通過類路徑中-cp的命令列選項:
java -cp lib/one.jar;lib/two.jar;/var/lib/three.jar path.to.MainClass |
Java執行時通過聚合來自所有引用的JAR的所有類並新增主類來建立類路徑。
分發依賴於其他JAR的JAR時會出現新的問題:
- 您需要在相同版本中定義相同的庫
- 更重要的是,該-cp引數不適用於JARs。要引用其他JAR,需要在JAR清單中通過Class-Path屬性設定類路徑:Class-Path: lib/one.jar;lib/two.jar;/var/lib/three.jar
- 因此,您需要根據清單將JAR放在目標檔案系統上的相對或絕對*相同的位置。這意味著要開啟JAR並先閱讀清單。
Apache Assembly外掛
Maven的Assembly Plugin使開發人員能夠將專案輸出組合到一個可分發的存檔中,該存檔還包含依賴項,模組,站點文件和其他檔案。
Assembly外掛依賴於特定的assembly.xml配置檔案。它允許您選擇要包含在工件中的檔案。請注意,最終的工件不必是JAR:配置檔案可讓您在可用格式(例如zip,war等)之間進行選擇。
該外掛通過提供預定義的程式集來管理常見的用例。自包含的JAR的分佈在其中。配置如下pom.xml所示:
<plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> <archive> <manifest> <mainClass>ch.frankel.blog.executablejar.ExecutableJarApplication</mainClass> </manifest> </archive> </configuration> <executions> <execution> <goals> <goal>single</goal> </goals> <phase>package</phase> </execution> </executions> </plugin> |
上述步驟:
- 參考預定義的自包含JAR配置
- 設定要執行的主類
- 執行single目標
- 將目標繫結到package階段,即在構建原始JAR之後
執行mvn package產生兩個工件:
- <name>-<version>.jar
- <name>-<version>-with-dependencies.jar
第一個JAR的內容與沒有該外掛時建立的內容相同。第二個是獨立的JAR。您可以像這樣執行它:
java -jar target/executable-jar-0.0.1-SNAPSHOT.jar
根據專案的不同,它可能會成功執行... 例如,它在示例Spring Boot專案中失敗,並顯示以下訊息:
%d [%thread] %-5level %logger - %msg%n java.lang.IllegalArgumentException: No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct. %d [%thread]%-5level%logger-%msg%n java.lang.IllegalArgumentException: 在META-INF/spring.factories中找不到自動配置類。 如果使用的是自定義包裝,請確保該檔案正確無誤。 |
原因是在同一路徑下不同的JAR提供不同的資原始檔,例如 META-INF/spring.factories。該外掛遵循最後寫入勝出策略覆蓋相同資原始檔。順序是基於JAR的名稱。
使用Assembly,您可以排除資源,但不能合併它們。當您需要合併資源時,您可能需要使用Apache Shade外掛。
Apache Shade外掛
Assembly外掛是通用的;Shade外掛僅專注於建立獨立的JAR的任務。
該外掛提供了將工件打包(包括其依賴項)並遮蔽(即重新命名)某些依賴項的包的功能。
該外掛基於轉換器的概念:每個轉換器負責處理一種型別的資源。轉換器可以按原樣複製資源,新增靜態內容,將其與其他資源合併等。
雖然您可以開發一個轉換器,但是該外掛提供了一組現成的轉換器:
ApacheLicenseResourceTransformer:防止許可證重複 ApacheNoticeResourceTransformer:準備合併 NOTICE AppendingTransformer:向資源新增內容 ComponentsXmlResourceTransformer:聚合多個 components.xml DontIncludeResourceTransformer:防止包含匹配資源 GroovyResourceTransformer:合併Apache Groovy擴充套件模組 IncludeResourceTransformer:從專案新增檔案 ManifestResourceTransformer:在 MANIFEST設定輸入項 PluginXmlResourceTransformer:聚合多個Maven配置 plugin.xml ResourceBundleAppendingTransformer:合併ResourceBundle ServicesResourceTransformer:META-INF/services資源中已重定位的類名並將其合併 XmlAppendingTransformer:將XML內容新增到XML資源 PropertiesTransformer:合併擁有序數的屬性檔案以解決衝突 OpenWebBeansPropertiesTransformer:合併Apache OpenWebBeans配置檔案 MicroprofileConfigTransformer:根據序號合併衝突的Microprofile配置屬性 |
上面程式集的Shade外掛配置如下:
<plugin> <artifactId>maven-shade-plugin</artifactId> <executions> <execution> <id>shade</id> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>ch.frankel.blog.executablejar.ExecutableJarApplication</mainClass> <manifestEntries> <Multi-Release>true</Multi-Release> </manifestEntries> </transformer> </transformers> </configuration> </execution> </executions> </plugin> |
上述配置說明:
- 預設情況下,shade目標已繫結到package階段
- 該轉換器專用於生成清單檔案
- 設定Main-Class條目
- 將最終的JAR配置為多發行版JAR。當任何初始JAR是多發行版JAR時,這都是必需的
執行mvn package產生兩個工件:
- <name>-<version>.jar:自包含的可執行檔案JAR
- original-<name>-<version>.jar:沒有嵌入式依賴項的“普通”
對於示例專案,最終的可執行檔案仍然無法按預期工作。確實,在構建期間有很多關於重複資源的警告。其中兩個阻止示例專案正常工作。為了正確地合併它們,我們需要看一下它們的格式:
- META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat:此Log4J2檔案包含預編譯的Log4J2外掛資料。它以二進位制格式編碼,任何開箱即用的轉換器都無法合併此類檔案。然而,隨便搜尋發現有人已經遇到了這個問題,併發布了一個轉換器來處理合並。
- META-INF/spring.factories:這些特定於Spring的檔案具有單鍵/多值格式。雖然它們是基於文字的,但沒有開箱即用的轉換器可以正確合併它們。但是,Spring開發人員在其外掛中提供了此功能(以及更多)。
要配置這些轉換器,我們需要將以上庫作為依賴項新增到Shade外掛中:
<plugin> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>ch.frankel.blog.executablejar.ExecutableJarApplication</mainClass> <manifestEntries> <Multi-Release>true</Multi-Release> </manifestEntries> </transformer> <transformer implementation="com.github.edwgiz.maven_shade_plugin.log4j2_cache_transformer.PluginsCacheFileTransformer" /> <transformer implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer"> <resource>META-INF/spring.factories</resource> </transformer> </transformers> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>com.github.edwgiz</groupId> <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId> <version>2.14.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.4.1</version> </dependency> </dependencies> </plugin> |
上述配置說明:
- 合併Log4J2.dat檔案
- 合併/META-INF/spring.factories檔案
- 新增所需的變壓器程式碼
此配置有效!仍然有剩餘警告:
- Manifests
- Licenses, notices and similar files
- Spring Boot specific files i.e. spring.handlers, spring.schemas and spring.tooling
- Spring Boot-Kotlin specific files e.g. spring-boot.kotlin_module, spring-context.kotlin_module, etc.
- Service loader configuration files
- JSON files
您可以新增和配置其他變壓器來修復其餘的警告。總而言之,整個過程需要對每種資源以及如何使用它們有深刻的理解。
Spring Boot外掛
Spring Boot外掛採用了完全不同的方法。它不會單獨合併來自JAR的資源。它增加了相關的JAR ,因為它們是 uber JAR。為了載入類和資源,它提供了一種特定的類載入機制。顯然,它專用於Spring Boot專案。
配置Spring Boot外掛很簡單:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.4.1</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> |
讓我們檢查一下最終JAR的結構:
/ |__ BOOT-INF | |__ classes | |__ lib |__ META-INF | |__ MANIFEST.MF |__ org |__ springframework |__ loader |
目錄說明:
- classes:專案編譯類
- lib:JAR依賴
- loader:Spring Boot類載入類
這是我們的示例專案清單檔案配置:
Main-Class: org.springframework.boot.loader.JarLauncher Start-Class: ch.frankel.blog.executablejar.ExecutableJarApplication |
如您所見,主類是特定於Spring Boot的類,而“真實”主類在另一個條目下被引用。
有關JAR結構的更多資訊,請參閱參考文件。
結論
在這篇文章中,我們描述了三種建立獨立的可執行JAR的方法:
- 組裝非常適合簡單的專案
- 當專案開始變得更加複雜並且您需要處理重複的檔案時,請使用Shade
- 最後,對於Spring Boot專案,最好的選擇是專用外掛
可以在Github上以Maven格式找到此帖子的完整原始碼。
相關文章
- springboot的jar為何能獨立執行Spring BootJAR
- Java建立多執行緒的一種方法Java執行緒
- Java基礎:執行緒的三種建立方式Java執行緒
- 建立執行緒的三種方式執行緒
- 將一個帶有main方法的jar生成為可執行jarAIJAR
- Java併發(三)----建立執行緒的三種方式及檢視程式執行緒Java執行緒
- IDEA打可執行的jar包IdeaJAR
- 【 Thread】建立執行緒的2種方法thread執行緒
- WPF打包獨立執行的程式
- Maven打包springboot框架的可執行的jarMavenSpring Boot框架JAR
- 建立執行緒的4種方法 and 執行緒的生命週期執行緒
- Java建立多執行緒的四種方式Java執行緒
- Java多執行緒【三種實現方法】Java執行緒
- 「有點收穫」三種基本方法建立執行緒執行緒
- 【java】【多執行緒】建立執行緒的兩種常用方式(2)Java執行緒
- Oracle提高SQL執行效率的三種方法ITOracleSQL
- 【Maven】構建可執行JarMavenJAR
- 終止java執行緒的2種方法Java執行緒
- Java建立多執行緒的幾種方式實現Java執行緒
- Java 中執行緒池的7種建立方式!Java執行緒
- Java中獲取JAR檔案中資源路徑的三種方法JavaJAR
- Java Executors類的9種建立執行緒池的方法及應用場景分析Java執行緒
- Java實現多執行緒的三種方式Java執行緒
- 併發程式設計 建立執行緒的三種方式程式設計執行緒
- Java 執行緒建立與常用方法Java執行緒
- 獨立站運營——獨立站的3種搭建方式
- C# Winform 使用Costura.Fody打包一個成可獨立執行的exeC#ORM
- 執行緒建立的四種方式執行緒
- JAVA中執行緒的建立Java執行緒
- Linux中執行Shell指令碼的方式(三種方法)Linux指令碼
- java 多執行緒之使用 interrupt 停止執行緒的幾種方法Java執行緒
- linux系統中怎麼中執行jar包?linux系統中執行jar包的方法LinuxJAR
- 徹底透析SpringBoot jar可執行原理Spring BootJAR
- C++中有三種建立物件的方法C++物件
- 【JAVA併發第二篇】Java執行緒的建立與執行,執行緒狀態與常用方法Java執行緒
- 執行緒池建立的幾種方式執行緒
- java的執行緒、建立執行緒的 3 種方式、靜態代理模式、Lambda表示式簡化執行緒Java執行緒模式
- VC啟動一個新執行緒的三種方法執行緒