一篇文章帶你搞定JAVA Maven

1、maven是什麼,為什麼存在?項目結構是什麼樣子,怎麼定位jar

官方網站說瞭好多,整的多復雜一樣,簡單說:maven是一個管理包的工具。

Maven 存在的必要性是什麼吶?想想開源的jar包如此之多,版本又如此之多,在沒有Maven之前,我們管理jar包全部都是下載之後創建一個lib的文件夾,然後項目進行引用,在其他的項目成員需要修改一個jar的時候需要到處拷貝,在部署的時候也很麻煩,問題存在就要解決,因此出現瞭Maven,統一管理,統一的倉庫,隻需要配置是要哪個版本的包,直接下載就夠瞭,不用拷貝,是不是很方便。

現在大的問題解決瞭,怎麼定位一個jar包吶?

圖片

2、Idea 的操作

1.新建maven項目

File ->新建->project

圖片

勾選從原型(模板)創建,選擇maven-archetype-qiuckstart

圖片

填入項目的名字,和groupId (公司域名反過來,如com.alibaba)

圖片

選擇本地倉庫的位置,和自定義的setting配置

圖片

一路finish,然後等待idea 創建模板項目就好瞭。

2.配置倉庫

Maven 倉庫有三種類型:

  • 本地(local)
  • 中央(central)
  • 遠程(remote)

當我們執行 Maven 構建命令時,Maven 開始按照以下順序查找依賴的庫

步驟 1 - 在本地倉庫中搜索,如果找不到,執行步驟 2,如果找到瞭則執行其他操作。

步驟 2 - 在中央倉庫中搜索,如果找不到,並且有一個或多個遠程倉庫已經設置,則執行步驟 4,如果找到瞭則下載到本地倉庫中以備將來引用。

步驟 3 - 如果遠程倉庫沒有被設置,Maven 將簡單的停滯處理並拋出錯誤(無法找到依賴的文件)。

步驟 4 - 在一個或多個遠程倉庫中搜索依賴的文件,如果找到則下載到本地倉庫以備將來引用,否則 Maven 將停止處理並拋出錯誤(無法找到依賴的文件)。

阿裡雲倉庫配置:

 <repositories>
       <repository>
           <id>central</id>
           <name>aliyun maven</name>
           <url>https://maven.aliyun.com/repository/public/</url>
           <layout>default</layout>
           <!-- 是否開啟發佈版構件下載 -->
           <releases>
               <enabled>true</enabled>
           </releases>
           <!-- 是否開啟快照版構件下載 -->
           <snapshots>
               <enabled>false</enabled>
           </snapshots>
       </repository>
   </repositories>

3.添加依賴,添加fastjson的依賴

舉個例子:

  <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
       </dependency>

4.打包項目

圖片

3、Maven坐標主要組成

  • groupId:組織標識(包名),一般常用公司域名的反序,比如com.alibaba
  • artifactId:項目名稱,項目的具體名稱
  • version:項目的當前版本 ,一般版本號為 大版本.小版本.小版本序號
  • packaging:項目的打包方式,最為常見的jar和war兩種

4、maven生命周期

4.1 名詞解釋

lifecycle:生命周期,這是maven最高級別的的控制單元,它是一系列的phase組成,也就是說,一個生命周期,就是一個大任務的總稱,不管它裡面分成多少個子任務,反正就是運行一個lifecycle,就是交待瞭一個任務,運行完後,就得到瞭一個結果,中間的過程,是phase完成的,自己可以定義自己的lifecycle,包含自己想要的phase

phase:可以理解為任務單元,lifecycle是總任務,phase就是總任務分出來的一個個子任務,但是這些子任務是被規格化的,它可以同時被多個lifecycle所包含,一個lifecycle可以包含任意個phase,phase的執行是按順序的,一個phase可以綁定很多個goal,至少為一個,沒有goal的phase是沒有意義的

goal: 這是執行任務的最小單元,它可以綁定到任意個phase中,一個phase有一個或多個goal,goal也是按順序執行的,一個phase被執行時,綁定到phase裡的goal會按綁定的時間被順序執行,不管phase己經綁定瞭多少個goal,你自己定義的goal都可以繼續綁到phase中

mojo: lifecycle與phase與goal都是概念上的東西,mojo才是做具體事情的,可以簡單理解mojo為goal的實現類,它繼承於AbstractMojo,有一個execute方法,goal等的定義都是通過在mojo裡定義一些註釋的anotation來實現的,maven會在打包時,自動根據這些anotation生成一些xml文件,放在plugin的jar包裡

可以通俗理解為lifecyle 是一個公司,phrase 是具體的部門,goal 是一個項目,Mojo 是項目內部的人,其他的都是管理層級,具體的執行還是人。

4.2 生命周期

圖片

4.3 goal 的概念

一個goal是獨立的,它可以被綁定到多個phase中去,也可以一個phase都沒有。如果一個goal沒有被綁定到任何一個lifecycle,它仍然可以直接被調用,而不是被lifecycle調用。

因此可以這樣理解phase與goal的關系:

1.phase其實就是goal的容器。實際被執行的都是goal。phase被執行時,實際執行的都是被綁定到該phase的goal。

2.goal與goal之間是獨立的。因此單獨執行一個goal不會導致其他goal被執行。

goal可以通俗理解為一個項目。

4.4 生命周期和phase的關系

clean生命周期每套生命周期都由一組階段(Phase)組成,我們平時在命令行輸入的命令總會對應於一個特定的階段。比如,運行mvn clean ,這個的clean是Clean生命周期的一個階段。有Clean生命周期,也有clean階段。Clean生命周期一共包含瞭三個階段:

1.pre-clean 執行一些需要在clean之前完成的工作

2.clean 移除所有上一次構建生成的文件

3.post-clean 執行一些需要在clean之後立刻完成的工作

“mvn clean” 中的clean就是上面的clean,在一個生命周期中,運行某個階段的時候,它之前的所有階段都會被運行,也就是說,”mvn clean”等同於 mvn pre-clean clean ,如果我們運行 mvn post-clean ,那麼 pre-clean,clean 都會被運行。這是Maven很重要的一個規則,可以大大簡化命令行的輸入

執行phase實際執行的是goal。如果一個phase沒有綁定goal,那這個phase就不會被執行。

<plugin>
  <groupId>com.mycompany.example</groupId>
  <artifactId>display-maven-plugin</artifactId>
  <version>1.0</version>
  <executions>
    <execution>
      <phase>process-test-resources</phase>
      <goals>
        <goal>time</goal>
      </goals>
    </execution>
  </executions>
</plugin>

一個生命周期包含一些列的步驟,當執行生命周期的時候,會把所有的步驟執行一次

官方文檔:

http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html

http://maven.apache.org/ref/3.3.9/maven-core/lifecycles.html

5、idea maven的配置

POM 中可以指定以下配置:

  • 項目依賴 dependencies
  • 插件 plugins
  • 執行目標
  • 項目構建 profile
  • 項目版本
  • 項目開發者列表
  • 相關郵件列表信息

6、POM有2個很重要的關系:聚合、繼承

一、聚合

如果我們想一次構建多個項目模塊,那我們就需要對多個項目模塊進行聚合

1.聚合配置代碼

<modules>
      <module>模塊一</module>
      <module>模塊二</module>
      <module>模塊三</module>
</modules>

  例如:對項目的Hello、HelloFriend、MakeFriends這三個模塊進行聚合

<modules>
      <module>../Hello</module>  
      <module>../HelloFriend</module>        
      <module>../MakeFriends</module>
</modules>

其中module的路徑為相對路徑。

二、繼承

  繼承為瞭消除重復的配置,我們把很多相同的配置提取出來,例如:grouptId,version,相同的依賴包等。

繼承配置代碼:

<parent>  
         <groupId>me.gacl.maven</groupId>
         <artifactId>ParentProject</artifactId>
         <version>0.0.1-SNAPSHOT</version>
         <relativePath>../ParentProject/pom.xml</relativePath>  
</parent>

Idea 中可以新建一個maven項目,然後刪光文件夾,隻留一個pom.xml,然後添加模塊,選擇繼承。

圖片

7、Maven 中的 profile

  • Maven 中有一個概念叫做:profile,它主要是為瞭解決不同環境所需的不同變量、配置等問題。比如我們內網開發的數據庫配置,端口配置等是和生產環境不同的,這個時候就需要區分。
  • 有瞭 profile,可以根據激活的條件,啟動不同條件下的配置信息。
  • profile 是可以有多個的,也可以同時激活多個 profile,方便自由組合。
<profiles>
       <profile>
           <!--不同環境Profile的唯一id-->
           <!--開發環境-->
           <id>dev</id>
           <properties>
               <!--profiles.active是自定義的字段(名字隨便起),自定義字段可以有多個-->
               <profiles.active>dev</profiles.active>
           </properties>
       </profile>
       <profile>
           <!--線上環境-->
           <id>prod</id>
           <properties>
               <profiles.active>prod</profiles.active>
           </properties>
           <activation>
               <activeByDefault>true</activeByDefault>
           </activation>
       </profile>
   </profiles>

圖片

Idea 中會顯示配置的兩個profile ,可以選擇激活

圖片

pom文件裡的配置為

<build>
       <resources>
           <resource>
               <directory>src/main/resources/</directory>
               <!--先排除掉兩個文件夾-->
               <excludes>
                   <exclude>dev/*</exclude>
                   <exclude>prod/*</exclude>
               </excludes>
               <includes>
                   <!--如果有其他定義通用文件,需要包含進來-->
                   <!--<include>messages/*</include>-->
               </includes>
           </resource>
           <resource>
               <!--這裡是關鍵!根據不同的環境,把對應文件夾裡的配置文件打包-->
               <directory>src/main/resources/${profiles.active}</directory>
           </resource>
       </resources>
   </build>
  <profiles>
       <profile>
           <!--不同環境Profile的唯一id-->
           <!--開發環境-->
           <id>dev</id>
           <properties>
               <!--profiles.active是自定義的字段(名字隨便起),自定義字段可以有多個-->
               <profiles.active>dev</profiles.active>
           </properties>
       </profile>
       <profile>
           <!--線上環境-->
           <id>prod</id>
           <properties>
               <profiles.active>prod</profiles.active>
           </properties>
           <activation>
               <activeByDefault>true</activeByDefault>
           </activation>
       </profile>
   </profiles>

8、maven 插件

Maven的核心僅僅定義瞭抽象的生命周期,具體的任務都是交由插件完成的。

每個插件都能實現多個功能,每個功能就是一個插件目標goal。

Maven的生命周期與插件目標相互綁定,以完成某個具體的構建任務,例如compile就是插件maven-compiler-plugin的一個插件目標。

常用插件:

maven-antrun-plugin maven-archetype-plugin maven-assembly-plugin maven-dependency-plugin maven-enforcer-plugin maven-help-plugin maven-release-plugin maven-resources-plugin maven-surefire-plugin build-helper-maven-plugin exec-maven-plugin jetty-maven-plugin versions-maven-plugin

9、環境變量

${basedir}表示項目根目錄,即包含pom.xml文件的目錄;

${version}表示項目版本;

${project.basedir}同${basedir};

${project.baseUri}表示項目文件地址;

${maven.build.timestamp}表示項目構件開始時間;

${maven.build.timestamp.format}表示屬性${maven.build.timestamp}的展示格式,默認值為yyyyMMdd-HHmm,可自定義其格式,其類型可參考java.text.SimpleDateFormat。

${project.build.directory}表示主源碼路徑;

${project.build.sourceEncoding}表示主源碼的編碼格式;

${project.build.sourceDirectory}表示主源碼路徑;

${project.build.finalName}表示輸出文件名稱;

${project.version}表示項目版本,與${version}相同;

${project.xxx} 當前pom文件的任意節點的內容

${env.xxx} 獲取系統環境變量。

${settings.xxx} 指代瞭settings.xml中對應元素的值。

10、Maven 依賴沖突的2個方法

1.統一版本

使用dependencyManagement 進行版本鎖定,dependencyManagement可以統一管理項目的版本號,確保應用的各個項目的依賴和版本一致。

如果我們項目中隻想使用spring core 5.2.0的包,pom.xml可以改為如下

<dependencyManagement>
       <dependencies>
           <dependency>
               <groupId>org.springframework</groupId>
               <artifactId>spring-core</artifactId>
               <version>5.2.0.RELEASE</version>
           </dependency>
       </dependencies>
   </dependencyManagement>
   <dependencies>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context</artifactId>
           <version>5.2.7.RELEASE</version>
       </dependency>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-aop</artifactId>
           <version>5.2.0.RELEASE</version>
       </dependency>
   </dependencies>

2.排除依賴

依賴查找的兩個原則:

使用路徑近者優先原則:直接依賴級別高於傳遞依賴。

使用第一聲明者優先原則:誰先定義的就用誰的傳遞依賴,即在pom.xml文件自上而下,先聲明的jar坐標,就先引用該jar的傳遞依賴。

Idea 可以安裝maven helper插件,解決沖突。

maven helper插件安裝成功,點開pom.xml會發現多瞭一個Dependency Analyzer視圖,如下上面按鈕的圖標含義如下

Conflicts(查看沖突)

All Dependencies as List(列表形式查看所有依賴)

All Dependencies as Tree(樹形式查看所有依賴)

上圖說明有3個jar存在沖突,點擊沖突的jar,可以查看和哪個jar產生沖突,如下圖

圖片

點開pom.xml,切換到Dependency Analyzer視圖,選擇All Dependencies as Tree,點擊要排除的jar,右鍵會出現Execlude選項,如下

圖片

11、scope

scope取值 有效范圍(compile, runtime, test) 依賴傳遞 例子
compile all spring-core
provided compile, test servlet-api
runtime runtime, test JDBC驅動
test test JUnit
system compile, test

compile :為默認的依賴有效范圍。如果在定義依賴關系的時候,沒有明確指定依賴有效范圍的話,則默認采用該依賴有效范圍。此種依賴,在編譯、運行、測試時均有效。

provided :在編譯、測試時有效,但是在運行時無效。例如:servlet-api,運行項目時,容器已經提供,就不需要Maven重復地引入一遍瞭。

runtime :在運行、測試時有效,但是在編譯代碼時無效。例如:JDBC驅動實現,項目代碼編譯隻需要JDK提供的JDBC接口,隻有在測試或運行項目時才需要實現上述接口的具體JDBC驅動。

test :隻在測試時有效,例如:JUnit。

system :在編譯、測試時有效,但是在運行時無效。和provided的區別是,使用system范圍的依賴時必須通過systemPath元素顯式地指定依賴文件的路徑。由於此類依賴不是通過Maven倉庫解析的,而且往往與本機系統綁定,可能造成構建的不可移植,因此應該謹慎使用。systemPath元素可以引用環境變量。

總結:

Maven是開發中常用的工具,很重要,所以盡可能的掌握。

本篇文章就到這裡瞭,希望能給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: