打包是一項神聖、而莊嚴的工作。package意味著我們離生產已經非常近了。它會把我們之前的大量工作濃縮成為一個、或者多個檔案。接下來,運維的同學就可以拿著這些個打包檔案在生產上縱橫四海了。
這麼一項莊嚴、神聖的工作,卻沒有受到多數人的關注,大家習慣去網上隨意copy一段pom的xml程式碼,往自己專案裡面一扔,然後就開始執行package打包了。大多數只知道,Maven幫助我管理了JAR包的依賴,可以自動下載,很方便。確實,因為它太方便了,很多時候,我們幾乎是沒有感知它的存在。想起來某個功能的時候,直接去使用就可以了。
而構建的工作其實並不簡單!例如:
- 打包後的程式,與生產環境JAR包衝突
- 依賴中有多個版本的依賴,如何選擇、排除依賴
- 編譯scala,某些JAR包的呼叫存在相容問題
- 如何根據不同的環境來載入不同的配置,例如:本地環境、叢集環境。
- 編譯開源專案報錯,根本無從下手解決。
- ...
其實,稍微離生產環境近一些,我們會發現很多的問題都暴露了出來。碰到這些問題的時候,當然可以第一時間百度。但為了能夠更精準的定位問題、減少打包時候給別人挖坑,我們還是很有必要來了解一些關於Maven的細節。
目錄
菜鳥玩dependency,神仙玩plugin
我們使用Maven的時候,95%的時候關注是dependency,而很少有人真正會花時間去研究Maven的plugin。但小猴要告訴大家,其實Maven工作的核心是plugin,而不是dependency。好吧!再直接一點,菜鳥玩dependency,神仙玩plugin。是不是拼命想要反駁我,大家看看官網Plugin在Maven文件的位置,這意味著什麼?
靈魂拷問:大家留意過嗎?是不是隻去官網上下載Maven,然後隨便百度一個教程就開始用Maven了?
分析Hadoop Example模組打包
學習的一種最好方式就是借鑑,借鑑優秀的開源專案。看看別人是怎麼做的。所以,接下來,我們就來看看Hadoop是如何打包的。為了方便給大家演示,小猴特意用Maven給大家演示一遍編譯、打包。這樣效果會明顯些。
操作步驟:
- 在github上找到Apahce/hadoop專案(https://github.com/apache/hadoop)
- 找到hadoop-mapreduce-project / hadoop-mapreduce-examples模組。
- 開啟pom.xml檔案。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-project</artifactId>
<version>3.4.0-SNAPSHOT</version>
<relativePath>../../hadoop-project</relativePath>
</parent>
<artifactId>hadoop-mapreduce-examples</artifactId>
<version>3.4.0-SNAPSHOT</version>
<description>Apache Hadoop MapReduce Examples</description>
<name>Apache Hadoop MapReduce Examples</name>
<packaging>jar</packaging>
<properties>
<mr.examples.basedir>${basedir}</mr.examples.basedir>
</properties>
<dependencies>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs-client</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-yarn-server-tests</artifactId>
<scope>test</scope>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-app</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-app</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-grizzly2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-hs</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.hadoop.thirdparty</groupId>
<artifactId>hadoop-shaded-guava</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>org.apache.hadoop.examples.ExampleDriver</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<configuration>
<findbugsXmlOutput>true</findbugsXmlOutput>
<xmlOutput>true</xmlOutput>
<excludeFilterFile>${mr.examples.basedir}/dev-support/findbugs-exclude.xml</excludeFilterFile>
<effort>Max</effort>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
<configuration>
<excludes>
<exclude>src/main/java/org/apache/hadoop/examples/dancing/puzzle1.dta</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
通過瀏覽hadoop example的xml檔案,我們發現了以下幾點:
-
所有的依賴都在父工程hadoop-project的pom.xml中定義好了。在hadoop example專案中,沒有出現任何一個版本號。
-
Hadoop使用了三個外掛,一個是maven-jar-plugin、一個是findbugs-maven-plugin、還有一個是apache-rat-plugin。
我們進入到example模組中pom.xml所在的目錄中,直接執行mvn package試試看。
[root@compile hadoop-mapreduce-examples]# mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------< org.apache.hadoop:hadoop-mapreduce-examples >-------------
[INFO] Building Apache Hadoop MapReduce Examples 3.2.1
[INFO] --------------------------------[ jar ]---------------------------------
Downloading from apache.snapshots.https: https://repository.apache.org/content/repositories/snapshots/org/apache/hadoop/hadoop-mapreduce-client-app/3.2.1/hadoop-mapreduce-client-app-3.2.1-tests.jar
.....
[INFO]
[INFO] --- maven-antrun-plugin:1.7:run (create-testdirs) @ hadoop-mapreduce-examples ---
[INFO] Executing tasks
main:
[INFO] Executed tasks
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hadoop-mapreduce-examples ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /opt/hadoop-3.2.1-src/hadoop-mapreduce-project/hadoop-mapreduce-examples/src/main/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hadoop-mapreduce-examples ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hadoop-mapreduce-examples ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /opt/hadoop-3.2.1-src/hadoop-mapreduce-project/hadoop-mapreduce-examples/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hadoop-mapreduce-examples ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-surefire-plugin:3.0.0-M1:test (default-test) @ hadoop-mapreduce-examples ---
Downloading from central: http://maven.aliyun.com/nexus/content/groups/public/org/apache/maven/surefire/surefire-junit4/3.0.0-M1/surefire-junit4-3.0.0-M1.jar
..........
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.apache.hadoop.examples.TestBaileyBorweinPlouffe
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.536 s - in org.apache.hadoop.examples.TestBaileyBorweinPlouffe
..........
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 11, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-jar-plugin:2.5:jar (default-jar) @ hadoop-mapreduce-examples ---
[INFO]
[INFO] --- maven-site-plugin:3.6:attach-descriptor (attach-descriptor) @ hadoop-mapreduce-examples ---
[INFO] Skipping because packaging 'jar' is not pom.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:11 min
[INFO] Finished at: 2021-02-06T10:49:18+08:00
[INFO] ------------------------------------------------------------------------
很快就編譯成功了,我們來看看Maven做了什麼:
1、執行maven-antrun-plugin外掛的run create-testdirs任務。奇怪的是,Example模組中並沒有引入該外掛。一會來看看該外掛在何處配置的。
[INFO] --- maven-antrun-plugin:1.7:run (create-testdirs) @ hadoop-mapreduce-examples ---
[INFO] Executing tasks
2、執行maven-resources-plugin外掛的resources任務,這個外掛應該是拷貝resource目錄到target的。
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hadoop-mapreduce-examples ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /opt/hadoop-3.2.1-src/hadoop-mapreduce-project/hadoop-mapreduce-examples/src/main/resources
3、執行maven-compiler-plugin外掛的compile任務,注意:現在才開始編譯程式碼。因為發現我們之前已經編譯過了,所以此處並沒有重新編譯class。
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hadoop-mapreduce-examples ---
[INFO] Compiling 47 source files to /opt/hadoop-3.2.1-src/hadoop-mapreduce-project/hadoop-mapreduce-examples/target/classes
4、執行maven-resources-plugin外掛的testResources任務,顧名思義,就是將單元測試相關的resource目錄拷貝到target。
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hadoop-mapreduce-examples ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /opt/hadoop-3.2.1-src/hadoop-mapreduce-project/hadoop-mapreduce-examples/src/test/resources
5、執行maven-compiler-plugin外掛的testCompile任務,同樣,將單元測試的檔案編譯一遍。
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hadoop-mapreduce-examples ---
[INFO] Compiling 7 source files to /opt/hadoop-3.2.1-src/hadoop-mapreduce-project/hadoop-mapreduce-examples/target/test-classes
6、執行maven-surefire-plugin外掛的test任務,開始執行單元測試。確保編譯的程式碼沒有問題。
INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.apache.hadoop.examples.TestBaileyBorweinPlouffe
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.436 s - in org.apache.hadoop.examples.TestBaileyBorweinPlouffe
[INFO] Running org.apache.hadoop.examples.TestWordStats
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.332 s - in org.apache.hadoop.examples.TestWordStats
[INFO] Running org.apache.hadoop.examples.pi.math.TestLongLong
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.163 s - in org.apache.hadoop.examples.pi.math.TestLongLong
[INFO] Running org.apache.hadoop.examples.pi.math.TestModular
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.164 s - in org.apache.hadoop.examples.pi.math.TestModular
[INFO] Running org.apache.hadoop.examples.pi.math.TestSummation
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.091 s - in org.apache.hadoop.examples.pi.math.TestSummation
[INFO] Running org.apache.hadoop.examples.terasort.TestTeraSort
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.449 s - in org.apache.hadoop.examples.terasort.TestTeraSort
7、執行maven-jar-plugin外掛的jar任務,這個任務是打包成jar檔案。
[INFO] --- maven-jar-plugin:2.5:jar (default-jar) @ hadoop-mapreduce-examples ---
[INFO] Building jar: /opt/hadoop-3.2.1-src/hadoop-mapreduce-project/hadoop-mapreduce-examples/target/hadoop-mapreduce-examples-3.2.1.jar
8、執行maven-site-plugin的attach-descriptor任務。該任務只有專案是pom打包時候才可用,將site.xml(site描述符)新增到部署的檔案列表中。
[INFO] --- maven-site-plugin:3.6:attach-descriptor (attach-descriptor) @ hadoop-mapreduce-examples ---
[INFO] Skipping because packaging 'jar' is not pom.
由此,我們可以發現,當我們執行一個package、compile、或者clean命令時,其實背後都是執行Maven的一個外掛。只不過有的外掛是Maven自帶的,直接可以使用,當我們需要自定義外掛的行為時,就需要顯示在pom.xml中顯式配置外掛了。
Maven中有大量的、豐富的外掛供開發人員使用。
地址:https://maven.apache.org/plugins/
我們可以點選任意一個plugin,檢視其具體的內容。
maven-antrun-plugin外掛
我們發現在example模組的父模組hadoop-project中有一個pom.xml。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>create-testdirs</id>
<phase>validate</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<mkdir dir="${test.build.dir}"/>
<mkdir dir="${test.build.data}"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
我們看到這裡面有配置一些外掛,其中就個maven-antrun-plugin。該外掛會執行run#create-testdirs任務,並且在validate階段執行。我們看到,該外掛執行了兩次mkdir。
maven-jar-plugin外掛
外掛配置如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>org.apache.hadoop.examples.ExampleDriver</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
前面,我們看到了是在執行package階段時自動執行的。並且指定了執行的主類是ExampleDriver。通過檢視打包後的JAR檔案,我們可以發現,JAR外掛只會將專案中的class檔案打包到JAR檔案中,並不會打包其他的依賴。
並且,在JAR包的META-INF(後設資料中),可以看到MAINFEST.MF檔案,已經生成了執行主類:
這個外掛的相關說明,可以參看官網:https://maven.apache.org/plugins/maven-jar-plugin/
JAR包中的META-INF目錄
在每個jar包中有一個META-INF目錄,顧名思義。它肯定是包含了JAR檔案的後設資料相關。Java基於META-INF目錄中的檔案來配置Java應用程式、類載入器以及其他服務。它包含以下內容:
MANIFEST.MF
用於定義副檔名以及打包相關的清單。
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: China
Created-By: Apache Maven 3.5.0
Build-Jdk: 1.8.0_241
Main-Class: cn.monkey.StreamingJob
該檔案中顯示了檔案的版本、由哪個使用者構建的、由哪個應用建立的、構建的JDK版本、以及非常重要的Main-Class。
INDEX.LIST
該檔案由JAR工具的-i選項生成,包括了應用程式或者擴充套件中定義的包的位置。用於類載入器加速類載入過程。
xxx.SF
JAR包的簽名檔案
xxx.DSA
與SF檔案關聯的簽名塊檔案。該檔案儲存了簽名檔案對應的數字簽名。
Maven外掛
Maven構建生命週期
Maven是一個專案管理工具,它把專案的構建主要分為了以下階段:
- validate:驗證專案是否正確,所有必要的資訊是否均已經提供
- compile:編譯專案的原始碼。
- test:執行單元測試。
- package:打包已編譯的程式碼。
- verify:對整合測試結果進行檢查,確保符合質量標準。
- install:將軟體包安裝到本地倉庫。
- deploy:將最終的軟體包複製到遠端倉庫,方便和其他開發人員共享。
也就是說,只要是一個Maven專案,從原始碼到一個可執行的程式,需要經歷著一系列的構建階段。而每個階段的背後,是Maven提供了一個構建過程的核心執行引擎,這個核心的專案構建執行引擎是由大量的外掛來執行具體的任務。
重新定義Maven
讓我們從技術角度,重新定義Maven——一個包含了很多外掛的框架。真正執行各種Maven操作的其實都是外掛。例如:
- 構建JAR包
- 構建WAR包
- 編譯程式碼
- 執行單元測試
- 建立專案文件
等等。只要是能夠想到需要在專案上執行的所有操作,其背後都是外掛實現的。
外掛是Maven的核心功能,一旦定義好了外掛,就可以在多個專案中重用。想想,我們是不是在每個pom.xml配置打包外掛、編譯外掛等等。找到pom.xml的位置,然後執行 package、comile、clean等操作即可。當我們執行:
mvn compile
的時候,Maven得知道,哦!當前要執行編譯了。由此可以知道,Maven的外掛是由mvn的引數來驅動的。這些引數定義了外掛的目標(或者Mojo)。
Mojo
Maven中最簡單的外掛是Clean Plugin。它只是負責刪除Maven專案的target目錄。但執行mvn clean
時,Maven將執行Clean外掛中定義的clean目標(Goal),並刪除目標目錄。Clean外掛還定義了可用於自定義外掛行為的引數,長概述為outputDirectory,預設為${project.build.directory}。
Mojo實際上是Maven外掛中的一個目標(Goal),一個外掛可以包含許多的Goal。我們可以用帶註解的Java類或者BeanShell指令碼來定義Mojo。它指定了Goal相關的後設資料:Goal名稱、以及Goal所執行的生命週期、以及引數。
檢視clean外掛原始碼
通過Maven官網的plugins連結,我們可以找到clean外掛在github的地址。
看到src目錄和pom.xml,我們就可以知道,原來Maven的plugin也是一個Maven標準專案。先來看看pom.xml裡面有什麼。為了方便觀察,我刪除了一些程式碼。
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugins</artifactId>
<version>34</version>
<relativePath/>
</parent>
<properties>
<mavenVersion>3.1.1</mavenVersion>
<javaVersion>7</javaVersion>
<surefire.version>2.22.2</surefire.version>
<mavenPluginToolsVersion>3.6.0</mavenPluginToolsVersion>
<project.build.outputTimestamp>2020-04-07T21:04:00Z</project.build.outputTimestamp>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>${mavenVersion}</version>
</dependency>
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-shared-utils</artifactId>
<version>3.2.1</version>
</dependency>
<!-- dependencies to annotations -->
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<scope>provided</scope>
</dependency>
<build>
<pluginManagement>
<plugins>
<!-- remove with next parent upgrade -->
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.1.1</version>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0-M3</version>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.9.1</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<profile>
<id>run-its</id>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-invoker-plugin</artifactId>
<configuration>
<debug>true</debug>
<addTestClassPath>true</addTestClassPath>
<projectsDirectory>src/it</projectsDirectory>
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
<pomIncludes>
<pomInclude>*/pom.xml</pomInclude>
</pomIncludes>
<preBuildHookScript>setup</preBuildHookScript>
<postBuildHookScript>verify</postBuildHookScript>
<localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
<settingsFile>src/it/settings.xml</settingsFile>
<goals>
<goal>clean</goal>
</goals>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
</profiles>
</project>
我們看到,pom.xml中引入了一些必要的依賴、以及定義了一些其他外掛的版本、在profile中,還定義了maven-invoker-plugin的配置。裡面配置了
Invoker外掛:用於執行一組Maven專案,該外掛可以確定每個專案執行是否成功,並且可以選擇驗證從給定專案執行生成的輸出。比較適合用於整合測試。
我們看到了原始碼中有一個CleanMojo的原始檔,程式碼中使用了註解來定義外掛的Goal和引數。
我們看到execute中就是執行clean目標,裡面呼叫了Cleaner來清理檔案。再檢視下install外掛的Mojo
install預設繫結的是INSTALL階段。
看完上面的原始碼,我們知道:以後使用外掛,可以看看它的Mojo檔案就知道它對應的目標是什麼、引數是什麼。我們還可以通過外掛的原始碼來進行錯誤排查。
分析Flink Archetype中的pom.xml
scala版本的pom.xml依賴要比Java版本要複雜,因為Maven預設就是用於構建Java的。而針對scala的構建,需要進行額外配置Maven支援。
mvn archetype:generate \
-DarchetypeGroupId=org.apache.flink \
-DarchetypeArtifactId=flink-quickstart-scala \
-DarchetypeVersion=1.12.1
Flink自動生成的程式碼如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.monkey</groupId>
<artifactId>flink_scala_demo_1.12.1</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Flink Quickstart Job</name>
<repositories>
<repository>
<id>apache.snapshots</id>
<name>Apache Development Snapshot Repository</name>
<url>https://repository.apache.org/content/repositories/snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<flink.version>1.12.1</flink.version>
<scala.binary.version>2.11</scala.binary.version>
<scala.version>2.11.12</scala.version>
<log4j.version>2.12.1</log4j.version>
</properties>
<dependencies>
<!-- Apache Flink dependencies -->
<!-- These dependencies are provided, because they should not be packaged into the JAR file. -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-scala_${scala.binary.version}</artifactId>
<version>${flink.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-scala_${scala.binary.version}</artifactId>
<version>${flink.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients_${scala.binary.version}</artifactId>
<version>${flink.version}</version>
<scope>provided</scope>
</dependency>
<!-- Scala Library, provided by Flink as well. -->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
<scope>provided</scope>
</dependency>
<!-- Add connector dependencies here. They must be in the default scope (compile). -->
<!-- Example:
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka_${scala.binary.version}</artifactId>
<version>${flink.version}</version>
</dependency>
-->
<!-- Add logging framework, to produce console output when running in the IDE. -->
<!-- These dependencies are excluded from the application JAR by default. -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- We use the maven-shade plugin to create a fat jar that contains all necessary dependencies. -->
<!-- Change the value of <mainClass>...</mainClass> if your program entry point changes. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<executions>
<!-- Run shade goal on package phase -->
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<excludes>
<exclude>org.apache.flink:force-shading</exclude>
<exclude>com.google.code.findbugs:jsr305</exclude>
<exclude>org.slf4j:*</exclude>
<exclude>org.apache.logging.log4j:*</exclude>
</excludes>
</artifactSet>
<filters>
<filter>
<!-- Do not copy the signatures in the META-INF folder.
Otherwise, this might cause SecurityExceptions when using the JAR. -->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>cn.monkey.StreamingJob</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<!-- Java Compiler -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- Scala Compiler -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
<configuration>
<args>
<arg>-nobootcp</arg>
</args>
</configuration>
</plugin>
<!-- Eclipse Scala Integration -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
<downloadSources>true</downloadSources>
<projectnatures>
<projectnature>org.scala-ide.sdt.core.scalanature</projectnature>
<projectnature>org.eclipse.jdt.core.javanature</projectnature>
</projectnatures>
<buildcommands>
<buildcommand>org.scala-ide.sdt.core.scalabuilder</buildcommand>
</buildcommands>
<classpathContainers>
<classpathContainer>org.scala-ide.sdt.launching.SCALA_CONTAINER</classpathContainer>
<classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
</classpathContainers>
<excludes>
<exclude>org.scala-lang:scala-library</exclude>
<exclude>org.scala-lang:scala-compiler</exclude>
</excludes>
<sourceIncludes>
<sourceInclude>**/*.scala</sourceInclude>
<sourceInclude>**/*.java</sourceInclude>
</sourceIncludes>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.7</version>
<executions>
<!-- Add src/main/scala to eclipse build path -->
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>src/main/scala</source>
</sources>
</configuration>
</execution>
<!-- Add src/test/scala to eclipse build path -->
<execution>
<id>add-test-source</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>src/test/scala</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
這是一個標準Flink Maven腳手架,Flink清晰地告訴了我們,哪些依賴是需要設定為provided、哪些是runtime,如果需要使用connector,需要自己額外引入對應不同儲存庫的connector。我們重點來分析外掛:
- maven-shade-plugin:可以看到,Flink是使用shade外掛進行fat jar打包的。可以通過mainClass引數配置jar包的入口。
- maven-compiler-plugin:配置Java編譯器。Flink預設使用1.8進行編譯。
- scala-maven-plugin:配置Scala編譯器。
- maven-eclipse-plugin:該外掛定義了編譯scala和java檔案
我們重點來看shade外掛。
注意:
如果多個外掛配置的生命週期階段為package,那麼會按照pom.xml的順序依次執行。
Shade外掛
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<executions>
<!-- Run shade goal on package phase -->
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<excludes>
<exclude>org.apache.flink:force-shading</exclude>
<exclude>com.google.code.findbugs:jsr305</exclude>
<exclude>org.slf4j:*</exclude>
<exclude>org.apache.logging.log4j:*</exclude>
</excludes>
</artifactSet>
<filters>
<filter>
<!-- Do not copy the signatures in the META-INF folder.
Otherwise, this might cause SecurityExceptions when using the JAR. -->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>cn.monkey.StreamingJob</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
Shade外掛可以將打包所有的artifact到一個uber-jar(uber-jar表示在一個JAR檔案中包含自身、以及所有的依賴)。Shade外掛只有一個goal:shade:shade。
可以看到,該外掛的生命週期配置在package,也就是執行package時,會自動執行。
在configuration中配置了要排除哪些artifacts。filter中配置了排除所有JAR包中的簽名檔案。我們可以在artifactSet、filter中來解決包衝突問題。
Assembly外掛
簡介
很多時候我們需要把專案打包成一個tar.gz包,就像Apache的一些元件一樣。通過使用Assembly外掛可以將程式、文件、配置檔案等等打包成一個“assemblies”。使用一個assembly descriptor可以描述整個過程。使用該外掛,可以把應用打包成以下型別:
zip
tar
tar.gz (or tgz)
tar.bz2 (or tbz2)
tar.snappy
tar.xz (or txz)
jar
dir
war
而如果要打包成user-jar,assembly外掛提供了一些基本的支援。官方建議還是使用shade外掛。Assembly外掛的使用步驟如下:
- 選擇或編寫一個assembly descriptor
- 在pom.xml檔案中配置assembly外掛
- 執行mvn assembly:single
Assembly介紹
Assembly(程式集)指的是一組檔案、目錄以及相關的依賴,為了方便軟體的安裝、部署、以及分發,我們可以把Assembly組織成一個zip包、或者tar.gz這種型別的包。例如:一個Maven專案中包含了控制檯應用和FX桌面客戶端應用。可以定義兩個Assembly,將應用和不同的指令碼、依賴繫結到一起。
針對Assembly,需要有一個Assembly Descriptor(程式集描述符),通過assembly descripor檔案可以描述將哪些檔案複製到bin目錄,並且可以修改目錄中檔案的許可權。
Goal
每一個Maven外掛都會有一個Goal,Assembly外掛也有一個Goal,那就是single,表示建立所有的Assembly。
分析Hadoop專案的Assembly外掛
Maven外掛配置
我們來看一下Hadoop中如何使用該外掛的。
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<inherited>false</inherited>
<executions>
<execution>
<id>src-dist</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<attach>false</attach>
<finalName>hadoop-${project.version}-src</finalName>
<outputDirectory>hadoop-dist/target</outputDirectory>
<!-- Not using descriptorRef and hadoop-assembly dependency -->
<!-- to avoid making hadoop-main to depend on a module -->
<descriptors>
<descriptor>hadoop-assemblies/src/main/resources/assemblies/hadoop-src.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
解釋下每個XML節點的意義:
配置項 | 說明 |
---|---|
appendAssemblyId | 設定為false表示從最終的輸出程式集中排除src-dist名字 |
attach | 控制Assembly外掛是否將生成的assembly附加到專案中 |
finalName | Assembly發行版最終的檔名 |
outputDirectory | Assembly檔案的最終輸出目錄 |
descriptors | 預設會從bin、jar-with-dependencies、src、project中載入內建描述符。 |
內建的descriptors可以從assembly.jar中載入。
可以參考https://maven.apache.org/plugins/maven-assembly-plugin/descriptor-refs.html看到所有內建的descriptor中說明。這裡Hadoop配置的是自己的descriptor。
大家可以參考外掛的AbstractAssemblyMojo.java中的定義。
hadoop-src.xml Assembly配置
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>hadoop-src</id>
<formats>
<format>tar.gz</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>.</directory>
<includes>
<include>LICENCE.txt</include>
<include>README.txt</include>
<include>NOTICE.txt</include>
</includes>
</fileSet>
<fileSet>
<directory>./licenses</directory>
<includes>
<include>*</include>
</includes>
</fileSet>
<fileSet>
<directory>.</directory>
<useDefaultExcludes>true</useDefaultExcludes>
<excludes>
<exclude>.git/**</exclude>
<exclude>**/.gitignore</exclude>
<exclude>**/.svn</exclude>
<exclude>**/*.iws</exclude>
<exclude>**/*.ipr</exclude>
<exclude>**/*.iml</exclude>
<exclude>**/.classpath</exclude>
<exclude>**/.project</exclude>
<exclude>**/.settings</exclude>
<exclude>**/target/**</exclude>
<!-- until the code that does this is fixed -->
<exclude>**/*.log</exclude>
<exclude>**/build/**</exclude>
<exclude>**/file:/**</exclude>
<exclude>**/SecurityAuth.audit*</exclude>
<exclude>patchprocess/**</exclude>
</excludes>
</fileSet>
</fileSets>
</assembly>
我們可以參考:https://maven.apache.org/plugins/maven-assembly-plugin/assembly.html看到assembly相關的所有配置。
配置項 | 配置 | 說明 |
---|---|---|
id | hadoop-src | 設定Assembly的id。 |
formats/format* | tar.gz | 指定assembly的最終格式。這裡hadoop配置的是tar.gz,表示最終打包出來一個tar.gz檔案,當然可以配置多個。例如:zip、tar、jar等。 |
includeBaseDirectory | true | 在tar.gz中包括一個base目錄。就是tar.gz會包含一個資料夾。而不是直接把大量的檔案直接放在tar.gz中。(這個一定要配置true,不然解壓安裝的時候會很蛋疼。) |
fileSets | fileset | 指定要包含在Assembly中的模組檔案。就是最終要將哪些檔案複製到tar.gz中。 |
fileset/useDefaultExcludes | true | 是否應使用標準排除模式。對於向後相容性,預設值為true。 |
fileset/directory | . | 設定模組目錄中的絕對或相對位置。例如,“src/main/bin”將選擇定義此依賴項的專案的子目錄。該目錄配置表示要打包哪個目錄下的檔案。 |
includes/include* | LICENCE.txt | 定義一組要包含的檔案和目錄。如果沒有配置,表示所有target內容 |
excludes/exclude* | **/.settings | 定義一組要排除的檔案和目錄。 |
製作一個屬於我們自己的打包程式
需求
我們通過編寫一個簡單的程式碼,然後將程式碼打包成類似於Apache的軟體包。程式碼非常簡單:
public class HelloMaven {
public static void main(String[] args) {
System.out.println("Hello! Maven Assembly Plugin!");
}
}
有一個配置檔案,我們也需要打包:
version=1.1.0
新增一個測試依賴
在pom.xml中新增以下:
<dependencies>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
一會我們會用shade外掛,將該依賴直接打成uber-jar。
配置shade外掛
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<executions>
<!-- Run shade goal on package phase -->
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>shell-scripts/*</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>cn.monkey.HelloMaven</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
執行package時,會先執行maven-jar-plugin。然後就會執行shade外掛了。注意:因為我們一會要使用assembly打包,將shade打包的user-jar直接打進tar.gz。所以,shade要配置在assembly外掛之前。
注意配置mainClass,也就是JAR的執行主類
配置Assembly外掛
配置Maven pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<inherited>false</inherited>
<executions>
<execution>
<id>assembly-test</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<attach>false</attach>
<finalName>${project.artifactId}-bin</finalName>
<outputDirectory>${project.build.directory}</outputDirectory>
<!-- Not using descriptorRef and hadoop-assembly dependency -->
<!-- to avoid making hadoop-main to depend on a module -->
<descriptors>
<descriptor>test-assemblies/test-descriptor.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
- finalName為最終打包的檔名,此處為artifactId加上bin字尾
- outputDirectory配置為Maven預設的輸出目錄,也就是一會打包完會自動在target目錄生成tar.gz
配置assembly descriptor
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>assembly-test</id>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.build.directory}</directory>
<useDefaultExcludes></useDefaultExcludes>
<outputDirectory>lib</outputDirectory>
<includes>
<include>*-shaded.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/classes</directory>
<outputDirectory>conf</outputDirectory>
<includes>
<include>**/config.properties</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/classes/shell-scripts</directory>
<outputDirectory>bin</outputDirectory>
<fileMode>755</fileMode>
<includes>
<include>**/start.sh</include>
</includes>
</fileSet>
</fileSets>
</assembly>
- 我們最終的檔案以tar.gz方式打包
- 第一個fileset為打包lib,我們最終的程式以jar包形式存放在tar.gz的lib資料夾中
- 第二個fileset打包配置檔案,這裡直接打包config.properties
- 第三個fileset打包執行的shell指令碼,並且配置了755可執行許可權
建立執行指令碼
在main目錄中新增shell-scripts/start.sh,要執行程式直接執行start.sh即可
#!/bin/bash
java -jar lib/${artifact.name}
配置資源打包
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
<resource>
<targetPath>${project.build.outputDirectory}/shell-scripts</targetPath>
<directory>src/main/shell-scripts</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
....
</build>
我們需要對shell-scriptrs下的指令碼進行變數替換。
配置profile
<project>
<profiles>
<profile>
<id>pro</id>
<properties>
<artifact.name>${project.artifactId}-${project.version}-shaded.jar</artifact.name>
</properties>
<activation>
<!-- 預設啟用該profile -->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
</profiles>
</project>
此處配置預設的Profile為pro,當打包時會用artifact.name屬性直接對shell-script中的指令碼進行替換。
執行打包
我把Maven的執行過程給大家解析一遍。
# 注意此處自動指定了profile為pro
C:\opt\jdk1.8.0_241\bin\java.exe -Dmaven.multiModuleProjectDirectory=C:\Users\China\Desktop\assembly-test -Dmaven.multiModuleProjectDirectory=$MAVEN_HOME -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -Dmaven.wagon.http.ssl.ignore.validity.dates=true -Dmaven.home=C:\Java\apache-maven-3.5.0 -Dclassworlds.conf=C:\Java\apache-maven-3.5.0\bin\m2.conf "-Dmaven.ext.class.path=C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3.2\plugins\maven\lib\maven-event-listener.jar" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=58840:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath C:\Java\apache-maven-3.5.0\boot\plexus-classworlds-2.5.2.jar org.codehaus.classworlds.Launcher -Didea.version=2020.3.2 package -P pro
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building assembly-test 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
# 拷貝resoource資原始檔
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ assembly-test ---
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 1 resource
[INFO] Copying 1 resource to C:\Users\China\Desktop\assembly-test\target\classes/shell-scripts
[INFO]
# 執行編譯外掛
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ assembly-test ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
# 執行test資源拷貝,當前為空
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ assembly-test ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory C:\Users\China\Desktop\assembly-test\src\test\resources
[INFO]
# 執行編譯測試用例程式碼
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ assembly-test ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
# 執行單元測試
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ assembly-test ---
[INFO] No tests to run.
[INFO]
# 執行Maven的預設jar打包
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ assembly-test ---
[INFO] Building jar: C:\Users\China\Desktop\assembly-test\target\assembly-test-1.0-SNAPSHOT.jar
[INFO]
# 執行shade外掛打包
[INFO] --- maven-shade-plugin:3.1.1:shade (default) @ assembly-test ---
[INFO] Including commons-cli:commons-cli:jar:1.2 in the shaded jar.
[INFO] Replacing original artifact with shaded artifact.
[INFO] Replacing C:\Users\China\Desktop\assembly-test\target\assembly-test-1.0-SNAPSHOT.jar with C:\Users\China\Desktop\assembly-test\target\assembly-test-1.0-SNAPSHOT-shaded.jar
[INFO] Dependency-reduced POM written at: C:\Users\China\Desktop\assembly-test\dependency-reduced-pom.xml
[INFO]
# 執行assembly外掛打包
[INFO] --- maven-assembly-plugin:2.2-beta-5:single (assembly-test) @ assembly-test ---
[INFO] Reading assembly descriptor: test-assemblies/test-descriptor.xml
[INFO] Building tar : C:\Users\China\Desktop\assembly-test\target\assembly-test-bin.tar.gz
[WARNING] Assembly file: C:\Users\China\Desktop\assembly-test\target\assembly-test-bin.tar.gz is not a regular file (it may be a directory). It cannot be attached to the project build for installation or deployment.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.202 s
[INFO] Finished at: 2021-02-06T21:19:21+08:00
[INFO] Final Memory: 15M/491M
[INFO] ------------------------------------------------------------------------
Process finished with exit code 0
是不是一目瞭然?
一頓操作後,我們發現確實打包成了tar.gz了。
用壓縮軟體開啟看一下:
全部都已經打包好了。而且shell指令碼已經進行了變數替換。
在Linux上部署
# 上傳到Linux伺服器
[root@compile assembly-test]# ll
總用量 40
-rw-r--r--. 1 root root 39611 2月 6 21:19 assembly-test-bin.tar.gz
# 解壓
[root@compile assembly-test]# tar -xvzf assembly-test-bin.tar.gz
assembly-test-bin/lib/assembly-test-1.0-SNAPSHOT-shaded.jar
assembly-test-bin/conf/config.properties
assembly-test-bin/bin/start.sh
# 執行
[root@compile assembly-test-bin]# pwd
/root/assembly-test/assembly-test-bin
[root@compile assembly-test-bin]# bin/start.sh
Hello! Maven Assembly Plugin!
是不是很酷~這樣的程式才是真正的、很正式的程式。
打包原始碼
最後為了方便小夥伴們測試,我們把Maven專案的原始碼打包一份。還是使用assembly外掛。
在Assembly中建立一個新的execution
<execution>
<id>test-source-descriptor</id>
<phase>compile</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<attach>false</attach>
<finalName>${project.artifactId}-source</finalName>
<outputDirectory>${project.build.directory}</outputDirectory>
<!-- Not using descriptorRef and hadoop-assembly dependency -->
<!-- to avoid making hadoop-main to depend on a module -->
<descriptors>
<descriptor>test-assemblies/test-source-descriptor.xml</descriptor>
</descriptors>
</configuration>
</execution>
注意:我當前配置的phase為compile,也就是編譯階段就會打包好maven的原始碼.
為打包原始碼配置assembly descriptor
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>test-source-descriptor</id>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}</directory>
<useDefaultExcludes></useDefaultExcludes>
<outputDirectory>.</outputDirectory>
<includes>
<include>src/**/*</include>
<include>test-assemblies/**/*</include>
<include>pom.xml</include>
</includes>
</fileSet>
</fileSets>
</assembly>
此處j將src原始碼目錄、test-assemblies下的所有檔案、以及pom.xml一塊打包給小夥伴。
執行compile
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building assembly-test 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ assembly-test ---
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 1 resource
[INFO] Copying 1 resource to C:\Users\China\Desktop\assembly-test\target\classes/shell-scripts
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ assembly-test ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-assembly-plugin:2.2-beta-5:single (test-source-descriptor) @ assembly-test ---
[INFO] Reading assembly descriptor: test-assemblies/test-source-descriptor.xml
[INFO] Building zip: C:\Users\China\Desktop\assembly-test\target\assembly-test-source.zip
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.274 s
[INFO] Finished at: 2021-02-06T21:38:37+08:00
[INFO] Final Memory: 13M/491M
[INFO] ------------------------------------------------------------------------
Process finished with exit code 0
大家看到assembly外掛已經執行,並將程式碼打包好了。
大家看到了,這才是我們做為一個開發應該玩的Maven。本次的案例程式碼在我公眾號上回復:maven-plugin即可獲取下載連結。大家自取之。
我們下期再見!
以上
參考文獻
[1] https://maven.apache.org/plugins/
[2] https://github.com/apache/hadoop
[3] https://maven.apache.org/guides/introduction/introduction-to-plugins.html
[4] https://github.com/apache/spark
[5] https://stackoverflow.com/questions/11947037/what-is-an-uber-jar
[6] https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html
[7] https://maven.apache.org/plugins/maven-assembly-plugin/index.html
[8] https://github.com/cko/predefined_maven_properties/blob/master/README.md