日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

Java 打包 FatJar 方法小结

發(fā)布時(shí)間:2023/11/29 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 打包 FatJar 方法小结 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

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

什么是 FatJar

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

三種打包方法

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

一個(gè)正常的 java 程序會(huì)有若干個(gè).class 文件和所依賴(lài)的第三方庫(kù)的 jar 文件組成。

1. 非遮蔽方法(Unshaded)

非遮蔽是相對(duì)于遮蔽而說(shuō)的,可以理解為一種樸素的辦法。解壓所有 jar 文件,再重新打包成一個(gè)新的單獨(dú)的 jar 文件。

借助 Maven Assembly Plugin 都可以輕松實(shí)現(xiàn)非遮蔽方法的打包。

Maven Assembly Plugin

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

預(yù)定義描述符如下

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

除了預(yù)定義的描述符,用戶(hù)也可以指定描述符,以滿(mǎn)足不同的打包需求。

打包成 uber-jar,需要使用預(yù)定義的 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 下打包一個(gè)非遮蔽的 jar 包,有不少插件可以用,但是由于 gradle 自身的靈活性,可以直接用 groove 的 dsl 實(shí)現(xiàn)。

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

非遮蔽方法會(huì)把所有的 jar 包里的文件都解壓到一個(gè)目錄里,然后在打包同一個(gè) fatjar 中。對(duì)于復(fù)雜應(yīng)用很可能會(huì)碰到同名類(lèi)相互覆蓋問(wèn)題。

2. 遮蔽方法(Shaded)

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

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 使用非常簡(jiǎn)單,簡(jiǎn)單聲明插件后就可以生效。

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

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

3. 嵌套方法(Jar of Jars)

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

Onejar Maven Plugin

One-JAR 就是一個(gè)基于上面嵌套 jar 實(shí)現(xiàn)的工具。onejar-maven-plugin 是社區(qū)基于 onejar 實(shí)現(xiàn)的 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 有點(diǎn)年久失修,好久沒(méi)有維護(hù)了,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 參數(shù)可以定制那些 jar 不希望被解壓,采用嵌套的方式打包到 Fatjar 內(nèi)部。

其打包后的內(nèi)部結(jié)構(gòu)為

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

應(yīng)用的類(lèi)文件被防止到 BOOT-INF/classes 目錄,依賴(lài)包被放置到 BOOT-INF/lib 目錄。

查看 META-INF/MANIFEST.MF 文件,其內(nèi)容為

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

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

但是函數(shù)計(jì)算需要的 jar 包是一種打包結(jié)構(gòu),在服務(wù)端運(yùn)行時(shí)會(huì)解壓開(kāi),不會(huì)調(diào)用 Main-Class。所以自定義 ClassLoader 是不生效的,所以不要使用嵌套 jar 結(jié)構(gòu),除非在入口函數(shù)指定重新定義 ClassLoader 或者 Classpath 以支持 BOOT-INF/classes 和 BOOT-INF/lib 這樣的定制化的類(lèi)路徑。

小結(jié)

插件構(gòu)建平臺(tái)工作機(jī)制
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 包內(nèi)部的自定義路徑解壓開(kāi)以后和函數(shù)計(jì)算是不兼容的。所以如果用于函數(shù)計(jì)算打包,建議使用 Unshaded 或者 Shared 的打包方式,但是需要自己注意文件覆蓋問(wèn)題。

參考閱讀

  • https://imagej.net/Uber-JAR
  • https://softwareengineering.stackexchange.com/questions/297276/what-is-a-shaded-java-dependency
  • https://docs.spring.io/spring-boot/docs/current/reference/html/executable-jar.html
  • 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

    總結(jié)

    以上是生活随笔為你收集整理的Java 打包 FatJar 方法小结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。