這次來說說maven
這玩意,同樣還是那句話,maven對我而言只是工具,一些常規操作已經足夠了,有空有興趣纔會去深入研究它。接下來會記錄下自己使用maven時需要注意
和理解
的地方,至於那些基本概念和環境配置的問題,相信大家都懂。
倉庫
maven倉庫可分爲本地倉庫
和遠程倉庫
。
如果公司自己有搭建maven私服,那麼還可以細分爲本地倉庫
、私服倉庫(內網)
、中央倉庫(外網)
。
私服是指公司內網搭建的maven倉庫,可供公司內部人員使用。
pom.xml裏依賴jar包的尋找流程:
-
本地倉庫找
,找到直接用,不需要聯網。 -
本地找不到,
私服倉庫找
,找到就下載到本地,以供下次直接使用。 -
私服找不到,會直接去
中央倉庫找
,然後下載到私服、本地,以供下次直接使用。
沒私服的話,本地倉庫找不到就直接去中央倉庫找。
構建過程
maven構建過程的各個環節,代表maven工作的某個階段
-
清理:將以前編譯得到的
舊的class字節碼文件刪除
,爲下一次編譯做準備。 -
編譯:將Java源程序
編譯成class字節碼
。 -
測試:自動測試,
自動調用junit程序
。 -
報告:測試程序執行的結果。
-
打包:動態Web工程
打war包
,java工程打jar包
。 -
安裝:將打包得到的文件
複製到倉庫中指定位置
。 -
發佈:
拷貝最終的工程包到遠程倉庫
,以供其他開發人員使用。
maven實際工作時,順序不一定從上到下。幾個階段是重要階段,並不是maven的全部階段。具體執行什麼階段,執行順序是啥,依賴於它的生命週期。
有些文章會說到最後一個構建階段:部署->將動態Web工程生成的war包複製到Servlet容器的指定目錄下,使其運行。 這需要在tomcat這種servlet容器配置點什麼,然後再執行maven相關命令,war包就會自動部署到容器下。不過我現在開發基本都是在SpringBoot環境下,war包部署就不存在了。有興趣再去熟悉熟悉。
生命週期
三套相互獨立
的生命週期,互不影響,定義了構建環節的執行順序
。
-
clean生命週期:構建之前進行一些
清理工作
。 -
default生命週期:
常用且核心
,包括編譯、測試、打包、安裝、發佈等 -
site生命週期:生成項目報告、站點。發佈站點。(我沒怎麼用過....)
依賴上述的構建過程和生命週期,maven執行任何一個階段的時候,它前面的所有階段都會執行
。
例如我們執行 mvn install 的時候,代碼會被編譯,測試,打包。但不會clean(清理),因爲install和clean是在不同的生命週期裏,但我們可以結合使用,如:mvn clean install
idea內置maven界面也說明這一點,點擊生命週期的某一個階段,maven會把前面到此階段都執行下,不信你可以試試。
maven命令
除了通過idea的界面操作maven,我們也可以手打命令,不然在linux系統上你怎麼搞?
-
清理 mvn clean
-
編譯 mvn compile
-
測試 mvn test
-
打包 mvn package
-
安裝 mvn install
-
發佈 mvn deploy
執行maven構建命令,必須在pom.xml所在的目錄
根據maven生命週期,當你執行mvn install時, compile、test、package、intall會依次執行,mvn deploy同理。
實際開發中,我都是直接敲命令編譯打包安裝,用得最多的是
mvn clean install -Dmaven.test.skip=true -U
加上clean是先把文件清理乾淨,100%確保install後是最新修改的文件。
如果當前項目並不需要被任何其他項目依賴,就沒必要install了,執行mvn clean package即可
mvn命令支持帶參數
-
-U :該參數能強制讓Maven檢查所有SNAPSHOT依賴更新,確保集成基於最新的狀態。
-
-Dmaven.test.skip=true :不執行測試用例,也不編譯測試用例類。
-
-DskipTests :不執行測試用例,但編譯測試用例類生成相應的class文件至target/test-classes下。
-
-P : 多環境打包(後面會說)
插件
maven倉庫除了保存jar包,還有插件。核心程序僅定義了抽象的生命週期,具體工作還是得由特定的插件來完成。
一般我使用maven-compiler-plugin 插件來編譯,定義的jdk版本一定要跟你開發使用的jdk版本一致,不然後續打包會出錯等各種奇怪問題。
pom.xml的build節點裏
<build>
<plugins>
<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>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
依賴範圍
標識jar包能被管理的範圍。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.3</version>
<scope>compile</scope>
</dependency>
-
compile:依賴項目參與編譯、測試、部署運行
-
test:僅參與測試,包括測試代碼的編譯、運行
-
provided:可以參與編譯、測試,但打包不會引入
一張表格來總結
compile | test | provided | |
---|---|---|---|
主程序 | ✓ | × | ✓ |
測試程序 | ✓ | ✓ | ✓ |
參與部署 | ✓ | × | × |
上述3個比較常見,還有另外3個不常見的
-
runtime:無需參與編譯、後期測試和運行週期需參與,一般用於數據庫驅動jar包
-
system:jar包從本地次磁盤路徑拿,不用在maven倉庫,一般加個systemPath節點表示具體路徑
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.3</version>
<scope>system</scope>
<systemPath>../xx/xx.jar</systemPath>
</dependency>
-
import:導入另一jar包的東西,僅支持在<dependencyManagement>中定義(後續有例子)
注意:非compile的依賴不能傳遞!
打包方式
packaging節點裏
<groupId>com.goku</groupId>
<artifactId>goku-manage</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
-
pom:依賴文件打包, 打出來可以作爲其他項目的maven依賴,子工程繼承父工程,子工程可直接使用父工程的配置,一般用在聚合工程來做jar包的版本控制。
-
jar:普通項目打包,開發時要引用通用類,打成jar包便於存放管理。SpringBoot內置tomcat,打成jar可直接運行。
-
war:web項目打包,打成war包部署到服務器。
聚合與繼承
聚合工程裏有多個pom.xml
父工程通過modules聚合子工程,該工程執行maven命令時,2個子工程也會執行
<modules>
<module>goku-user</module>
<module>goku-order</module>
</modules>
子工程通過parent標籤繼承父工程,可直接引用父工程的配置項
<parent>
<groupId>com.goku</groupId>
<artifactId>goku-manage</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
至於聚合模塊的好處有很多,真真切切讓我感受到的有其二
-
依賴包劃分合理,職責結構清晰,不存在一個龐大的pom.xml。
-
內部代碼改動後只需要編譯當前工程,耗時減少。特別適合分佈式項目。
統一版本號
聚合工程裏,版本號可以統一配置,統一管理。同個公司的不同項目,所有依賴包版本最好相同。
有些公司會單獨建一個工程,只用來統一版本號,打包成pom形式,給公司內部各個項目繼承。
頂層父工程的pom文件裏的properties節點
<properties>
<java.version>1.8</java.version>
<plugin.jdk.version>1.8</plugin.jdk.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>2.1.3.RELEASE</spring-boot.version>
<fasterjson.version>1.2.12</fasterjson.version>
</properties>
dependency的vesion引用配置好的版本號
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
dependencyManagement統一管理版本
請注意<scope>import</scope>
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.0.0.RELEASE</version>
<type>pom</type>
<!--導入spring-boot-dependencies裏的配置,一定程度上解決了單繼承問題。-->
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fasterjson.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
說明:
-
dependencyManagement只是聲明依賴,不會引入依賴。
-
子模塊不會繼承父模塊中dependencyManagement所有預定義的depandency,若想使用還需引入需要的依賴,只不過不需要加version節點了。
-
據說在父模塊中嚴禁直接使用depandencys預定義依賴,應該做到按需引入。
多環境打包
項目打包時區分環境,實際上是根據不同環境生成不同的配置文件。
一般區分本地、開發/測試、線上。
pom.xml裏的build和profiles(以下配置僅針對SpringBoot的yml文件)
<build>
<!-- 打包後文件名稱:項目名-環境-版本 -->
<finalName>${project.artifactId}</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<!-- 開啓過濾替換功能-->
<filtering>true</filtering>
<includes>
<!-- 項目打包完成的包中只包含當前環境文件 -->
<include>application.yml</include>
<include>application-${profileActive}.yml</include>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
<!-- 多環境配置方案 -->
<profiles>
<profile>
<id>local</id>
<properties>
<profileActive>local</profileActive>
</properties>
<activation>
<!-- 默認情況下使用本地開發配置 如 打包時不包含 -p 參數-->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!-- 打包命令package -P test -->
<profile>
<id>test</id>
<properties>
<profileActive>test</profileActive>
</properties>
</profile>
<!-- 打包命令package -P prod -->
<profile>
<id>prod</id>
<properties>
<profileActive>prod</profileActive>
</properties>
</profile>
</profiles>
maven命令帶參數-P,如:mvn clean install -P test
項目打包後只保留application.yml、ymlapplication-test.yml
application.yml裏的@profileActive@也會被替換成test
最終打包生成的文件
setting.xml
localRepository
指定本地倉庫地址,不配置就放在默認路徑${user.home}/.m2/repository
<localRepository>D:\develop\maven\Repmaven</localRepository>
server
公司搭建了maven私服,上傳下載需要認證,就需要配置。
NOTE: You should either specify username/password OR privateKey/passphrase, since these pairings are used together.
可配置賬號密碼 或 私鑰形式,支持多個server
<servers>
<server>
<!--id需要與repository或mirror中的id相對應-->
<id>server01</id>
<username>admin</username>
<password>SwaDmin159</password>
</server>
<server>
<id>server02</id>
<privateKey>/xxx/.ssh/id_dsa</privateKey>
<passphrase>some_passphrase</passphrase>
<filePermissions>664</filePermissions>
<directoryPermissions>775</directoryPermissions>
<configuration></configuration>
</server>
</servers>
mirror
倉庫鏡像,中央倉庫的代理,如果配置了鏡像,就直接去鏡像中下載依賴。它相當於一個攔截器,攔截maven對默認中央倉庫的請求,把請求裏的遠程倉庫地址,重定向到mirror裏配置的地址。
<mirrors>
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>central</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
</mirrors>
profile and activeProfiles
所有項目使用私服倉庫,可在setting.xml配置,profile節點配置repositories,最後再激活profile。
當然,私服裏不只一個倉庫,可配置多個repository。
<profile>
<repositories>
<repository>
<!-- id需要與私服裏倉庫的id對應 -->
<id>public</id>
<name>public</name>
<url>http://xxx.com/nexus/content/groups/public/</url>
<layout>default</layout>
</repository>
</repositories>
</profile>
<activeProfiles>
<activeProfile>public</activeProfile>
</activeProfiles>
若只想針對某個項目,則在當前項目的pom.xml配置私服,repositories節點下配置即可
<repositories>
<repository>
<!-- id需要與私服裏倉庫的id對應 -->
<id>public</id>
<name>public</name>
<url>http://xxx.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
distributionManagement
有時我們自己開發的模塊需要被其他部門的開發人員使用,就需要發佈到私服。
執行mvn deploy前,需配置distributionManagement。
快照版本跟發佈版本分別存儲到不同倉庫地址。
<distributionManagement>
<repository>
<id>release</id>
<name>Project Release</name>
<url>http://xxx.com/nexus/content/groups/release/</url>
</repository>
<snapshotRepository>
<id>snapshots</id>
<name>Project SNAPSHOTS</name>
<url>http://xxx.com/nexus/content/groups/snapshot/</url>
</snapshotRepository>
</distributionManagement>
快照版本vs發佈版本
maven會根據jar包的版本號(version節點)是否帶有-SNAPSHOT來判斷是快照版本還是正式版本。沒有SNAPSHOT都認爲是快照版本。
快照版本 :1.0.0-SNAPSHOT
發佈版本 :1.5.3.RELEASE
如果是快照版本,在mvn deploy時會自動發佈到快照版本庫中,覆蓋老的快照版本,後面使用快照版本模塊時,在不更改版本號的情況下,直接編譯打包時,maven會自動從遠程服務器上下載最新的快照版本。
如果是正式發佈版本,在mvn deploy時會自動發佈到正式版本庫中,後面使用正式版本的模塊,在不更改版本號的情況下,編譯打包時如果本地已經存在該版本的模塊則不會主動去遠程服務器上下載。
這個也容易理解,快照版本一般定義爲不穩定版本,需要實時更新。
作者:悟空GoKu
鏈接:https://juejin.im/post/5ef719805188252e53636058