一、maven-compiler-plugin 插件詳解
1.maven插件介紹
maven是項目構建管理工具,如果不明確指定源代碼要使用什么版本的jdk的話,maven-compiler-plugin編譯插件就使用默認版本進行編譯,這就容易出現編譯版本與源代碼不匹配,甚至導致編譯不通過。
例如:如果代碼中使用了Java 8 新特性,如函數式編程,但編譯時使用Java 7,那就完全不可能編譯通過,但如果編譯時使用java11則可以編譯通過,java高版本jdk的語法是向下兼容的。
為了避免使用低版本jdk編譯高版本java語法的情況,在構建項目時就要配置maven-compiler-plugin插件,指定項目源碼的 jdk 版本,編譯后的 jdk 版本,以及編碼方式。
從 maven-compiler-plugin-3.8.0 之后,默認 編譯 版本由 1.5 改為 1.6 。但這仍然跟不上 JDK 的更新速度,目前大多數系統都在使用 JDK 1.8。Apache Maven Project 對 maven-compiler-plugin中compiler:compile有如下關於jdk版本變更的描述:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<!--
一般而言,target與source是保持一致的,但是,有時候為了讓程序能在其他版本的jdk中運行
(比如:源代碼使用的是1.5語法,但想在1.8的jdk環境運行,由低到高是允許的,因為jdk語法是向下兼容,
對於低版本目標jdk,源代碼中不能使用低版本jdk中不支持的語法,會存在target不同於source的情況
-->
<source>1.8</source><!-- 源碼語法版本 -->
<target>1.8</target><!-- 目標語法版本 -->
<encoding>UTF-8</encoding><!-- 字符集編碼 -->
<skipTests>true</skipTests><!-- 跳過測試 -->
<verbose>true</verbose><!-- 詳細的編譯輸出 -->
<showWarnings>true</showWarnings><!-- 顯示警告信息 -->
<!-- 要使compilerVersion標簽生效,需要將fork設為true,用於明確表示編譯版本配置的可用 -->
<fork>true</fork>
<!-- 指定javac路徑,例如:<executable>${JAVA_1_4_HOME}/bin/javac</executable> -->
<executable>javac路徑</executable>
<compilerVersion>1.3</compilerVersion><!-- 指定插件將使用的編譯器的版本 -->
<meminitial>128m</meminitial><!-- 編譯器使用的初始內存 -->
<maxmem>512m</maxmem><!-- 編譯器使用的最大內存 -->
<!-- 這個選項用來傳遞編譯器自身不包含但是卻支持的參數選項 -->
<compilerArgument>-verbose -bootclasspath ${java.home}\lib\rt.jar</compilerArgument>
</configuration>
</plugin>
2.全局配置maven JDK編譯版本
此種設置方式將影響由maven創建的所有項目。找到maven的settings.xml配置文件,加入如下配置片段:
<profiles>
<profile>
<id>default-jdk8-env</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>8</jdk>
</activation>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<maven.compiler.compilerVersion>8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
二、org.apache.maven.plugins系列插件簡介
1.插件知識簡介
maven-compiler-plugin插件3.0之前,默認使用 JDK 自帶的 javac 進行編譯。但從3.0開始(需要JDK1.6),默認使用的 Java 編譯器 是javax.tools.JavaCompiler。如果仍然希望使用 JDK 自帶的 javac 編譯源代碼,需要為 mvn 指定啟動參數:-Dmaven.compiler.forceJavacCompilerUse=true
maven-compiler-plugin插件編譯時和編譯后運行的JVM版本目前默認的設置為1.5,默認用此版本,而不是根據你在IDEA項目結構中指定的工程JDK版本編譯。如果想改變這些默認設置,應該設置編譯源和目標中java編譯器的目標,通過設置Java源代碼兼容的JVM版本,標明Java源代碼開發過程中使用的Java版本,通過設置編譯后的類庫擬運行的JVM版本,給出編譯后的類庫將要運行的Java環境(一般都會設置,因為很少有項目再用1.7以下的版本了):

同時 mvn 的運行需要依賴 JDK ,maven-compiler-plugin 插件默認使用當前運行 mvn 命令的 JDK 去編譯Java源代碼。如果想使用其他版本的 JDK (比如本地 java 環境的,而非maven自帶的)編譯Java源代碼,則需要設置如下(重點fork 、executable、compilerVersion):

上述配置中,用於編譯 Java 源代碼的是 JDK 1.8 ,運行mvn命令時指定maven所使用的是JDK1.6
2.官網也建議指定編譯的jdk版本
如果是web項目,就需要打war包,那就需要這個插件:

使用maven工具鏈:
使用不同的JDK的最好方法是使用工具鏈方式。在建立一個項目,如編譯java源文件,生成Javadoc,運行單元測試、打包,這些插件中的每一個都需要一個JDK工具來對應操作:Javac、JavaDoc、JaveNeR等。使用Maven工具鏈插件,您可以為所有相關的Maven插件配置1個默認JDK工具鏈也可以各自配置不同的jdk,用法略。
配置編譯插件:
除工具鏈方式之外,也可以在編譯過程中使用的特定JDK。這樣的配置對這個插件是特定的,不會影響其他插件。compilerVersion參數可以用來指定插件使用的編譯器版本,但是需要將fork設置為true才能工作,此為非常用配置不做詳細了解。
針對不同的編譯器設置source和target選項:
有時編譯某個項目時需要使用的jdk與當前maven所使用的版本不同。Javac可以使用source和target參數來接受這樣的命令。編譯器插件也可以被配置為在編譯期間提供這些選項。如剛才上述:官網也建議指定編譯的jdk版本

傳遞編譯參數:
有時需要傳遞其他編譯器參數,這些編譯器參數本身不是插件本身需要處理的,而是需要將編譯器參數傳遞給Javac編譯器,如下圖

3.POM簡介
所有的 POM 都繼承自一個父 POM(無論是否顯式定義了這個父 POM),父POM包含一些可以被繼承的默認設置。Maven 使用 effective pom(Super pom 加上工程自己的配置)來決定最終執行編譯的行為,目的為了使開發者在pom.xml中做盡可能少的配置,且在子配置中可以被方便的覆蓋:
比如不指定packaging時,即默認打jar包時打開effective.pom:

再看另一種舉例:當指定packaging為war,指定打war包時打開effective pom

所以只需要指定 packaging 打包類型,maven插件可以自動加載並繼承父pom相關配置。
如果父pom中的默認配置不符合現有項目要求,而在我們的pom中有沒有覆蓋,則會報錯,如刪除pom中的maven-compiler-plugin,使其不覆蓋父pom中的maven-compiler-plugin,這樣就是使用的父pom中的配置:

上圖刪除了pom中的maven-compiler-plugin,再看下圖effecrive pom中的:

看到這里就使用了默認父pom的maven-compiler-plugin2.3.2版本,此版本默認的jdk是1.5,maven編譯時報:

然后修改自己的pom中的maven-compiler-plugin覆蓋父pom中的maven-compiler-plugin,使用自定義覆蓋默認配置:

上圖pom中增加了maven-compiler-plugin,再看下圖effecrive pom中的:

編譯成功
4.打開Show Effective POM的兩種方式
- maven 命令行方式將內容輸出到文件:
mvn help:effective-pom -Doutput=EffectivePom.xml
都知道 maven 約定大於配置,也就是默認有很多約定好的配置,能不改動就經量別改,比如 java 源代碼放置在 src/main/java下, 資源文件放置在src/main/resources下, 所以當我們把源代碼,資源按約定的結構建立起來后,pom.xml配置很少就可以build jar/war/ear 包, 那么如果你想知道pom.xml的默認配置有哪些,分別設置了哪些值,那么你可以通過上面的goal來生成一個完整的EffectivePom.xml文件,這里面有完整的配置.
打開Show Effective POM(最終生效POM配置,包含了繼承的內容)
- IDEA方式:


5.常用插件
Maven官網插件地址:https://maven.apache.org/plugins/index.html
maven 的屬性值的占位符,類似EL,類似 ant 的屬性,比如${X},可用於pom文件任何賦值的位置。
有以下分類:
env.X:操作系統環境變量,比如${env.PATH}
project.x:pom文件中的屬性,比如:1.0,引用方式:${project.version}
settings.xml:文件中的屬性,比如:false,引用方式:${settings.offline}
Java System Properties:java.lang.System.getProperties()中的屬性,比如java.home,引用方式:${java.home}
6.工作機制
Maven強大的一個重要的原因是它有一個十分完善的生命周期模型,它有三套相互獨立的生命周期,請注意這里說的是“三套”,請別將Maven的生命周期看成一個整體哦,三個生命周期是獨立線性執行,分別是:
Clean Lifecycle 在進行真正的構建之前進行一些清理工作。
Default Lifecycle 構建的核心部分,編譯,測試,打包,部署等等。
Site Lifecycle 生成項目報告,站點,發布站點。
每個生命周期包含一些階段(phase),這些階段(phase)是有順序的,每個階段蘊含一個或多個目標(goal),並且后面的階段依賴於前面的階段,我們和Maven最直接的交互方式就是調用這些生命周期階段。較之於生命周期階段的前后依賴關系,三套生命周期本身是相互獨立的,用戶可以僅僅調用clean生命周期的某個階段,或者僅僅調用default生命周期的某個階段,而不會對其他生命周期產生任何影響。例如,當用戶調用clean生命周期的clean階段的時候,不會觸發default生命周期的任何階段。其中deault是最重要的生命周期,擁有validate 、compile 、test 、package、integration、verify、install、deploy等等階段。
看一下Maven的編譯階段,讓maven進行編譯代碼,使用的是聲明的方式來告知Maven如何做的。看似一個簡單的命令,但其實它后面執行了一系列的工作。mvn compile如不指定compile階段的goal,所有complie階段所有goal,compile和test compile都會執行。
Maven是如何知道從哪里找到要編譯的源文件?並且Maven如何知道將編譯好的類文件放到哪里?這里就是由Mave基礎工作之一“通過配置進行約定”所解決的問題。一般情況下,源文件放在src/main/java路徑下,這種默認設置(雖然在上面的POM文件中並沒看到)是從父 POM繼承來的,即使最簡單的POM也知道源文件的默認位置
當首次執行compile命令或其它命令時,maven會下載所有插件和相關的文件,而之后再執行同一個命令的時候會發現比第一次快很多,這就為什么首次執行命令時候會比較慢的原因。
7.自定義maven插件
https://blog.csdn.net/qq_34272760/article/details/127148290
三、springboot 打包插件spring-boot-maven-plugin打包機制及內部結構分析
1.前言
許多公司都會選擇使用springboot作為服務應用開發框架,springboot框架提供了一套自己的打包機制,是通過spring-boot-maven-plugin插件來實現的。
1、spring-boot-maven-plugin引入pom
對於新建的一個springboot項目來說,pom中會加入插件:

通過idea可以看到maven中包含了spring-boot-maven-plugin插件:

功能說明:
- build-info:生成項目的構建信息文件 build-info.properties
- repackage:這個是默認
goal,在mvn package執行之后,這個命令再次打包生成可執行的 jar,同時將mvn package生成的 jar 重命名為*.origin - run:這個可以用來運行 Spring Boot 應用
- start:這個在
mvn integration-test階段,進行Spring Boot應用生命周期的管理 - stop:這個在
mvn integration-test階段,進行Spring Boot應用生命周期的管理
spring-boot-maven-plugin插件默認在父工程sprint-boot-starter-parent中被指定為repackage,可以點擊sprint-boot-starter-parent進入父pom進行查看,如下圖:

如果需要設置其他屬性,需要在當前應用的pom中進行設置去覆蓋父pom默認的值去改變行為。
2.執行打包命令
mvn clean package
或者通過開發工具如idea執行clean和package倆命令:

執行以上命令時會自動觸發spring-boot-maven-plugin插件的repackage目標,完后可以在target目錄下看到生成的jar,如下圖:

這里可以看到生成了兩個jar相關文件,其中common.jar是spring-boot-maven-plugin插件重新打包后生成的可執行jar,即可以通過java -jar common.jar命令啟動。common.jar.original這個則是mvn package打包的原始jar,在spring-boot-maven-plugin插件repackage命令操作時重命名為xxx.original,這個是一個普通的jar,可以被引用在其他服務中。
3.jar內部結構
對這兩個jar文件解壓看看里面的結構差異:
3.1 common.jar目錄結構如下:

其中BOOT-INF主要是一些啟動信息,包含classes和lib文件,classes文件放的是項目里生成的字節文件class和配置文件,lib文件是項目所需要的jar依賴。
META-INF目錄下主要是maven的一些元數據信息,MANIFEST.MF文件內容如下:
MANIFEST.MF:這個manifest文件定義了與擴展和包相關的數據。單詞“manifest”的意思是“顯示”。
Manifest-Version: 1.0
Implementation-Title: java-common-utils
Implementation-Version: 0.0.1-SNAPSHOT
Start-Class: com.common.util.CommonUtilsApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.1.9.RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org.springframework.boot.loader.JarLauncher
其中Start-Class是項目的主程序入口,即main方法。Springboot-Boot-Classes和Spring-Boot-Lib指向的是生成的BOOT-INF下的對應位置。
Main-Class屬性值為org.springframework.boot.loader.JarLauncher,這個值可以通過設置屬性layout來控制,如下:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--使用-Dloader.path需要在打包的時候增加<layout>ZIP</layout>,不指定的話-Dloader.path不生效-->
<layout>ZIP</layout>
<!-- 指定該jar包啟動時的主類[建議] -->
<mainClass>com.common.util.CommonUtilsApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
設置<layout>ZIP</layout>時Main-Class為org.springframework.boot.loader.PropertiesLauncher,具體layout值對應Main-Class關系如下:
- JAR,即通常的可執行jar
Main-Class: org.springframework.boot.loader.JarLauncher
- WAR,即通常的可執行war,需要的servlet容器依賴位於WEB-INF/lib-provided
Main-Class: org.springframework.boot.loader.warLauncher
- ZIP,即DIR,類似於JAR(打增量包時會使用到)
Main-Class: org.springframework.boot.loader.PropertiesLauncher
- MODULE,將所有的依賴庫打包(scope為provided的除外),但是不打包Spring Boot的任何Launcher
- NONE,將所有的依賴庫打包,但是不打包Spring Boot的任何Launcher
common.jar之所以可以使用java -jar運行,和MANIFEST.MF文件里的配置關系密切
3.2 common.jar.original目錄結構

可以看到通過mvn package構建的jar是一個普通的jar,包含的都是項目的字節文件和一些配置文件,沒有將項目依賴的第三方jar包含進來。再看下MANIFEST.MF文件:
Manifest-Version: 1.0
Implementation-Title: java-common-utils
Implementation-Version: 0.0.1-SNAPSHOT
Build-Jdk-Spec: 1.8
Created-By: Maven Archiver 3.4.0
其中沒有包含Start-Class、Main-Class等信息,這個與可執行jar的該文件存在很多差異,而且目錄結構也有很大差異。
一般對使用spring-boot-maven-plugin插件打出的可執行jar不建議作為jar給其他服務引用,因為可能出現訪問可執行jar中的一些配置文件找不到的問題。如果想讓構建出來的原始jar不被重新打包,可以對spring-boot-maven-plugin插件配置classifier屬性,自定義一個可運行jar名稱,這樣該插件就不會對原始的jar重命名操作了。
例如:
<configuration>
<!-- [建議]指定該jar包啟動時的主類 -->
<mainClass>com.common.util.CommonUtilsApplication</mainClass>
<!--配置的 classifier 表示可執行 jar 的名字,配置了這個之后,在插件執行 repackage 命令時,
就不會給 mvn package 所打成的 jar 重命名了,這樣就可以被其他項目引用了,classifier命名的為可執行jar-->
<classifier>myexec</classifier>
</configuration>
效果如下:

以上是對spring-boot-maven-plugin插件的打包機制和jar包結構的一些分析。
4.參考
https://blog.csdn.net/iss_jin/article/details/122463390
四、springboot增量打包更新--靜態資源分離打包
1.前言
springboot部署打包為jar,一般都是全量打包,生成的jar包因包含大量三方庫通常都是超過100M的,並且在進行一般的頁面html微調、js修改、img替換、css樣式修改時也需要重新打包進行部署;每次微小的調整都需要重新打包就太煩了,一般在項目開發穩定以后項目中引用的jar就不再改變
為了方便進行靜態資源管理及增量部署,對項目引用jar包以及靜態資源分離打包,提高打包的效率及部分前端微調項修改后及時進行無重啟更新;
2.具體步驟
1、初次打包進行全量打包,對打包的jar進行解壓,解壓后的文件如下圖展示:

2、分離引用的jar包:進入BOOT-INF中,copy文件夾lib到指定目錄下,如jar包運行目錄;


3分離靜態文件:在lib同目錄下創建resource文件夾,進入classes文件夾內copy文件夾static及templates文件夾到resource文件下;如圖:

4、刪除jar包及解壓文件,當前目錄結構如下:

5、增量打包,打包不再將引用jar及static文件夾、templates文件夾打到jar包中:首先修改pom.xml文件中打包相關配置,如下圖:
打包配置增加了如下代碼:
<layout>ZIP</layout>
<includes>
<include>
<groupId>non-exists</groupId>
<artifactId>non-exists</artifactId>
</include>
</includes>
resource打包配置增加如下過濾:
<exclude>static/**/*</exclude>
<exclude>templates/**/*</exclude>
最終pom.xml中打包配置如下:
<build>
<finalName>web</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
<mainClass>com.XXX.Application</mainClass>
<!--增量打包配置【start】-->
<layout>ZIP</layout>
<includes>
<include>
<groupId>non-exists</groupId>
<artifactId>non-exists</artifactId>
</include>
</includes>
<!--增量打包配置【end】-->
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.4.RELEASE</version>
<configuration>
<fork>false</fork>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<!--【增加靜態文件過濾】-->
<exclude>static/**/*</exclude>
<exclude>templates/**/*</exclude>
</excludes>
</resource>
</resources>
</build>
6、執行maven clean install,獲得最終jar包,如下圖所示,只有5M大小;

7、最終運行時,jar的執行命令中增加lib及resource的路徑指向,否則項目無法正常運行;
java -Dloader.path=./lib,./resource -jar ./web.jar
8、如上進行增量打包后,如果前端有不涉及到后端的修改時都可以對resource中的文件進行替換進行實時更新,不再進行重啟;后端有變動也不用上傳100多M的jar到服務器,影響效率;
3.參考
轉自:https://blog.csdn.net/qq_35611143/article/details/107083164
