一篇文章帶你搞定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的更多內容!