Java 打包 FatJar 方法小結

在函數計算(Aliyun FC)中發布一個 Java 函數,往往需要將函數打包成一個 all-in-one 的 zip 包或者 jar 包。Java 中這種打包 all-in-one 的技術常稱之為 Fatjar 技術。本文小結一下 Java 里打包 FatJar 的若干種方法。

什么是 FatJar

FatJar 又稱作 uber-Jar,是包含所有依賴的 Jar 包。Jar 包中嵌入了除 java 虛擬機以外的所有依賴。我們知道 Java 的依賴分為兩種, 零散的 .class 文件和把多個 .class 文件以 zip 格式打包而成 jar 文件。FatJar 是一個 all-in-one Jar 包。FatJar 技術可以讓那些用于最終發布的 Jar 便于部署和運行。

三種打包方法

我們知道 .java 源碼文件會被編譯器編譯成字節碼.class 文件。Java 虛擬機執行的是 .class 文件。一個 java 程序可以有很多個 .class文件。這些 .class 文件可以由 java 虛擬機的類裝載器運行期裝載到內存里。java 虛擬機可以從某個目錄裝載所有的 .class 文件,但是這些零散的.class 文件并不便于分發。所有 java 支持把零散的.class 文件打包成 zip 格式的 .jar 文件,并且虛擬機的類裝載器支持直接裝載 .jar 文件。

一個正常的 java 程序會有若干個.class 文件和所依賴的第三方庫的 jar 文件組成。

1. 非遮蔽方法(Unshaded)

非遮蔽是相對于遮蔽而說的,可以理解為一種樸素的辦法。解壓所有 jar 文件,再重新打包成一個新的單獨的 jar 文件。

借助 Maven Assembly Plugin 都可以輕松實現非遮蔽方法的打包。

Maven Assembly Plugin

Maven Assembly Plugin 是一個打包聚合插件,其主要功能是把項目的編譯輸出協同依賴,模塊,文檔和其他文件打包成一個獨立的發布包。使用描述符(descriptor)來配置需要打包的物料組合。并預定義了常用的描述符,可供直接使用。

預定義描述符如下

  • bin 只打包編譯結果,并包含 README, LICENSE 和 NOTICE 文件,輸出文件格式為 tar.gz, tar.bz2 和 zip。
  • jar-with-dependencies 打包編譯結果,并帶上所有的依賴,如果依賴的是 jar 包,jar 包會被解壓開,平鋪到最終的 uber-jar 里去。輸出格式為 jar。
  • src 打包源碼文件。輸出格式為 tar.gz, tar.bz2 和 zip。
  • project 打包整個項目,除了部署輸出目錄 target 以外的所有文件和目錄都會被打包。輸出格式為 tar.gz, tar.bz2 和 zip。

除了預定義的描述符,用戶也可以指定描述符,以滿足不同的打包需求。

打包成 uber-jar,需要使用預定義的 jar-with-dependencies 描述符:

在 pom.xml 中加入如下配置

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><version>CHOOSE LATEST VERSION HERE</version><configuration><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs></configuration><executions><execution><id>assemble-all</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions>
</plugin>

Gradle Java plugin

gradle 下打包一個非遮蔽的 jar 包,有不少插件可以用,但是由于 gradle 自身的靈活性,可以直接用 groove 的 dsl 實現。

apply plugin: 'java'jar {from {(configurations.runtime).collect {it.isDirectory() ? it : zipTree(it)}}
}

非遮蔽方法會把所有的 jar 包里的文件都解壓到一個目錄里,然后在打包同一個 fatjar 中。對于復雜應用很可能會碰到同名類相互覆蓋問題。

2. 遮蔽方法(Shaded)

遮蔽方法會把依賴包里的類路徑進行修改到某個子路徑下,這樣可以一定程度上避免同名類相互覆蓋的問題。最終發布的 jar 也不會帶入傳遞依賴沖突問題給下游。

Maven Shade Plugin

在 pom.xml 中加入如下配置

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.1.1</version><configuration><!-- put your configurations here --></configuration><executions><execution><phase>package</phase><goals><goal>shade</goal></goals></execution></executions></plugin>

Gradle Shadow plugin

Gradle shadow plugin 使用非常簡單,簡單聲明插件后就可以生效。

plugins {id 'com.github.johnrengelman.shadow' version '2.0.4'id 'java'
}shadowJar {include '*.jar'include '*.properties'exclude 'a2.properties'
}

遮蔽方法依賴修改 class 的字節碼,更新依賴文件的包路徑達到規避同名同包類沖突的問題,但是改名也會帶來其他問題,比如代碼中使用 Class.forName 或 ClassLoader.loadClass 裝載的類,Shade Plugin 是感知不到的。同名文件覆蓋問題也沒法杜絕,比如META-INF/services/javax.script.ScriptEngineFactory不屬于類文件,但是被覆蓋后會出現問題。

3. 嵌套方法(Jar of Jars)

還是一種辦法就是在 jar 包里嵌套其他 jar,這個方法可以徹底避免解壓同名覆蓋的問題,但是這個方法不被 JVM 原生支持,因為 JDK 提供的 ClassLoader 僅支持裝載嵌套 jar 包的 class 文件。所以這種方法需要自定義 ClassLoader 以支持嵌套 jar。

Onejar Maven Plugin

One-JAR 就是一個基于上面嵌套 jar 實現的工具。onejar-maven-plugin 是社區基于 onejar 實現的 maven 插件。

<plugin><groupId>com.jolira</groupId><artifactId>onejar-maven-plugin</artifactId><version>1.4.4</version><executions><execution><goals><goal>one-jar</goal></goals></execution></executions>
</plugin>

Spring boot plugin

One-JAR 有點年久失修,好久沒有維護了,Spring Boot 提供的 Maven Plugin 也可以打包 Fatjar,支持非遮蔽和嵌套的混合模式,并且支持 maven 和 gradle 。

<plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><layout>ZIP</layout><requiresUnpack><dependency><groupId>org.jruby</groupId><artifactId>jruby-complete</artifactId></dependency></requiresUnpack></configuration>
</plugin>
plugins {id 'org.springframework.boot' version '2.0.4.RELEASE'
}bootJar {requiresUnpack '**/jruby-complete-*.jar'
}

requiresUnpack 參數可以定制那些 jar 不希望被解壓,采用嵌套的方式打包到 Fatjar 內部。

其打包后的內部結構為

example.jar|+-META-INF|  +-MANIFEST.MF+-org|  +-springframework|     +-boot|        +-loader|           +-<spring boot loader classes>+-BOOT-INF+-classes|  +-mycompany|     +-project|        +-YourClasses.class+-lib+-dependency1.jar+-dependency2.jar

應用的類文件被防止到 BOOT-INF/classes 目錄,依賴包被放置到 BOOT-INF/lib 目錄。

查看 META-INF/MANIFEST.MF 文件,其內容為

Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.mycompany.project.MyApplication

啟動類是固定的 org.springframework.boot.loader.JarLauncher,應用程序的入口類需要配置成 Start-Class。這樣做的目的主要是為了支持嵌套 jar 包的類裝載,替換掉默認的 ClassLoader。

但是函數計算需要的 jar 包是一種打包結構,在服務端運行時會解壓開,不會調用 Main-Class。所以自定義 ClassLoader 是不生效的,所以不要使用嵌套 jar 結構,除非在入口函數指定重新定義 ClassLoader 或者 Classpath 以支持 BOOT-INF/classes 和 BOOT-INF/lib 這樣的定制化的類路徑。

小結

插件構建平臺工作機制
maven-assembly-pluginmavenUnshaded
Gradle Java plugingradleUnshaded
maven-shade-pluginmavenShaded
com.github.johnrengelman.shadowgradleShaded
Onejarant, mavenJar of Jars
Spring boot pluginmaven, gradleUnshaded, Jar of Jars

單從 Fatjar 的角度看, Spring boot maven/gradle 做得最精致。但是 jar 包內部的自定義路徑解壓開以后和函數計算是不兼容的。所以如果用于函數計算打包,建議使用 Unshaded 或者 Shared 的打包方式,但是需要自己注意文件覆蓋問題。

參考閱讀

  1. https://imagej.net/Uber-JAR
  2. https://softwareengineering.stackexchange.com/questions/297276/what-is-a-shaded-java-dependency
  3. https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/389796.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/389796.shtml
英文地址,請注明出處:http://en.pswp.cn/news/389796.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

常見問題及解決方案(前端篇)

一、jquery validate 默認校驗規則序號 規則 描述1 requiredtrue 必須輸入的字段。2 remote "check.php" 使用 ajax 方法調用 check.php 驗證輸入值。3 emailtrue 必須輸入正確格式的電子郵件。4 urltrue 必須輸入正確格式的網址。5 datetrue 必須輸入正確格式的日期…

本地搜索文件太慢怎么辦?用Everything搜索秒出結果(附安裝包)

每次用電腦本地的搜索都慢的一批&#xff0c;后來發現了一個搜索利器 基本上搜索任何文件都不用等待。 并且頁面非常簡潔&#xff0c;也沒有任何廣告&#xff0c;用起來非常舒服。 軟件官網如下&#xff1a; voidtools 官網提供三個版本&#xff0c;用起來差別不大。 網盤鏈…

2024. 考試的最大困擾度

2024. 考試的最大困擾度 一位老師正在出一場由 n 道判斷題構成的考試&#xff0c;每道題的答案為 true &#xff08;用 ‘T’ 表示&#xff09;或者 false &#xff08;用 ‘F’ 表示&#xff09;。老師想增加學生對自己做出答案的不確定性&#xff0c;方法是 最大化 有 連續相…

小程序入口傳參:關于帶參數的小程序掃碼進入的方法

1.使用場景 1.醫院場景&#xff1a;比如每個醫生一個id&#xff0c;通過帶參數二維碼&#xff0c;掃碼二維碼就直接進入小程序醫生頁面 2.餐廳場景&#xff1a;比如每個菜一個二維碼&#xff0c;通過掃碼這個菜的二維碼&#xff0c;進入小程序后&#xff0c;可以直接點這道菜&a…

python的power bi轉換基礎

I’ve been having a great time playing around with Power BI, one of the most incredible things in the tool is the array of possibilities you have to transform your data.我在玩Power BI方面玩得很開心&#xff0c;該工具中最令人難以置信的事情之一就是您必須轉換數…

感想3-對于業務邏輯復用、模板復用的一些思考(未完)

內容概覽&#xff1a; 業務邏輯復用的目的基于現有場景&#xff0c;如何抽象出初步可復用邏輯復用業務邏輯會不會產生過度設計的問題業務邏輯復用的目的 我對于業務邏輯復用的理解是忽略實際業務內容&#xff0c;從交互流程、交互邏輯的角度去歸納、總結&#xff0c;提出通用的…

Git的一些總結

.git 目錄結構 |── HEAD|── branches // 分支|── config // 配置|── description // 項目的描述|── hooks // 鉤子| |── pre-commit.sample| |── pre-push.sample| └── ...|── info| └── exclude // 類似.gitignore 用于排除文件|── objects // 存儲了…

2025. 分割數組的最多方案數

2025. 分割數組的最多方案數 給你一個下標從 0 開始且長度為 n 的整數數組 nums 。分割 數組 nums 的方案數定義為符合以下兩個條件的 pivot 數目&#xff1a; 1 < pivot < nnums[0] nums[1] … nums[pivot - 1] nums[pivot] nums[pivot 1] … nums[n -1] 同時…

您是六個主要數據角色中的哪一個

When you were growing up, did you ever play the name game? The modern data organization has something similar, and it’s called the “Bad Data Blame Game.” Unlike the name game, however, the Bad Data Blame Game is played when data downtime strikes and no…

命令查看linux主機配置

查看cpu&#xff1a; # 總核數 物理CPU個數 X 每顆物理CPU的核數 # 總邏輯CPU數 物理CPU個數 X 每顆物理CPU的核數 X 超線程數# 查看物理CPU個數 cat /proc/cpuinfo| grep "physical id"| sort| uniq| wc -l# 查看每個物理CPU中core的個數(即核數) cat /proc/cpui…

C#中全局處理異常方式

using System; using System.Configuration; using System.Text; using System.Windows.Forms; using ZB.QueueSys.Common;namespace ZB.QueueSys {static class Program{/// <summary>/// 應用程序的主入口點。/// </summary>[STAThread]static void Main(){Appli…

5911. 模擬行走機器人 II

5911. 模擬行走機器人 II 給你一個在 XY 平面上的 width x height 的網格圖&#xff0c;左下角 的格子為 (0, 0) &#xff0c;右上角 的格子為 (width - 1, height - 1) 。網格圖中相鄰格子為四個基本方向之一&#xff08;“North”&#xff0c;“East”&#xff0c;“South”…

自定義按鈕動態變化_新聞價值的變化定義

自定義按鈕動態變化I read Bari Weiss’ resignation letter from the New York Times with some perplexity. In particular, I found her claim that she “was hired with the goal of bringing in voices that would not otherwise appear in your pages” a bit strange: …

Linux記錄-TCP狀態以及(TIME_WAIT/CLOSE_WAIT)分析(轉載)

1.TCP握手定理 2.TCP狀態 l CLOSED&#xff1a;初始狀態&#xff0c;表示TCP連接是“關閉著的”或“未打開的”。 l LISTEN &#xff1a;表示服務器端的某個SOCKET處于監聽狀態&#xff0c;可以接受客戶端的連接。 l SYN_RCVD &#xff1a;表示服務器接收到了來自客戶端請求…

677. 鍵值映射

677. 鍵值映射 實現一個 MapSum 類&#xff0c;支持兩個方法&#xff0c;insert 和 sum&#xff1a; MapSum() 初始化 MapSum 對象 void insert(String key, int val) 插入 key-val 鍵值對&#xff0c;字符串表示鍵 key &#xff0c;整數表示值 val 。如果鍵 key 已經存在&am…

算法 從 數中選出_算法可以選出勝出的nba幻想選秀嗎

算法 從 數中選出Note from Towards Data Science’s editors: While we allow independent authors to publish articles in accordance with our rules and guidelines, we do not endorse each author’s contribution. You should not rely on an author’s works without …

jQuery表單校驗

小小Demo&#xff1a; <script>$(function () {//給username綁定失去焦點事件$("#username").blur(function () {//得到username文本框的值var nameValue $(this).val();//每次清除數據$("table font:first").remove();//校驗username是否合法if (n…

5912. 每一個查詢的最大美麗值

5912. 每一個查詢的最大美麗值 給你一個二維整數數組 items &#xff0c;其中 items[i] [pricei, beautyi] 分別表示每一個物品的 價格 和 美麗值 。 同時給你一個下標從 0 開始的整數數組 queries 。對于每個查詢 queries[j] &#xff0c;你想求出價格小于等于 queries[j] …

django-rest-framework第一次使用使用常見問題

2019獨角獸企業重金招聘Python工程師標準>>> 記錄在第一次使用django-rest-framework框架使用時遇到的問題&#xff0c;為了便于理解在這里創建了Person和Grade這兩個model from django.db import models class Person(models.Model):SHIRT_SIZES ((S, Small),(M, …

插入腳注把腳注標注刪掉_地獄司機不應該只是英國電影歷史數據中的腳注,這說明了為什么...

插入腳注把腳注標注刪掉Cowritten by Andie Yam由安迪(Andie Yam)撰寫 Hell Drivers”, 1957地獄司機 》電影海報 Data visualization is a great way to celebrate our favorite pieces of art as well as reveal connections and ideas that were previously invisible. Mor…