一.概述
1.1 目前技術存在的問題
1.一個項目就是一個工程
如果項目非常龐大,就不適合繼續使用package來劃分模塊。最好是每一個模塊對應一個項目,利用分工協作。藉助於Maven就可以將一個項目拆分成多個工程。
2.項目中需要的jar包必須手動“複製”,“粘貼”到WEB-INF/lib項目下
同樣的jar包文件重複出現在不同的項目工程中,一方面是浪費存儲空間,另外也讓工程比較臃腫。藉助Maven,可以將jar包僅僅保存在倉庫中,有需要使用的工程“引用”這個文件,並不需要重複複製。
3.jar包需要別人幫我們準備好,或到官網下載
所有知名框架或第三方工具jar包已經按照統一規範放在Maven的中央倉庫中。
4.一個jar包依賴的其他jar包需要自己手動加到項目中
Maven會自動將被依賴的jar包導入進來。
1.2 Maven是什麼?
Maven 是 Apache 軟件基金會組織維護的一款自動化構建工具,專注服務於 Java 平臺的項目構建和依賴管理 。
構建:就是以我們編寫的Java代碼、框架配置文件、國際化等其他資源文件、jsp頁面和圖片等靜態資源作爲“原材料”,去“生產”出一個可以運行的項目的過程。
構建過程中的幾個主要環節
①清理:刪除以前的編譯結果,爲重新編譯做好準備。
②編譯:將Java源程序編譯爲字節碼文件。
③測試:針對項目中的關鍵點進行測試,確保項目在迭代開發過程中關鍵點的正確性。
④報告:將每一次測試後以標準的格式記錄和展示測試結果。
⑤打包:將一個包含諸多文件的工程封裝爲一個壓縮文件用於安裝或部署。Java工程對應jar包,Web工程對象war包。
⑥安裝:在Maven環境下特指將打包的結果——Jar包或War包安裝到本地倉庫中。
⑦部署:將打包的結果部署到遠程倉庫或將war包部署到服務器上運行。
1.3 安裝Maven核心程序
1.檢查JAVA_HOME環境變量
2.解壓Maven核心程序的壓縮包,放在一個非中文、無空格 的路徑下。
3.配置Maven相關的環境變量
①MAVEN_HOME 或 M2_HOME
②path
4.驗證:運行 mvn -v 命令查看Maven版本
Maven的核心程序中僅僅定義了抽象的生命週期,而具體操作則是由Maven的插件來完成的,可以Maven的插件並不包含在Maven的核心程序中,首次使用需要聯網下載。
下載得到的插件會被保存在本地倉庫中,本地倉庫的默認位置是:~\.m2\repositoy。
二.Maven的核心概念
2.1約定的目錄結構
約定的目錄結構對於Maven實現自動化構建是必不可少的一個環節,就拿自動編譯來說,Maven必須找到java 源文件,下一步才能編譯,而編譯之後也必須有一個準確的位置保持編譯得到的字節碼文件。
我們在開發中如果需要讓第三方工具或框架知道我們創建的資源在哪,那麼基本就是兩種方式
1)以配置的方式明確告訴框架,如:
< param-value>classpath:spring-context.xml < /param-value>
2)遵守框架內部已經存在的約定,如如log4j的配置文件名規定必須爲 log4j.properties 或 log4j.xml 。
注意:約定>配置>編碼
Maven對工程目錄結構的要求就是屬於後面一種。
2.2 常用的Maven命令
執行與構建過程相關的Maven命令,必須進入pom.xml 所在的目錄。
有構建過程相關:編譯,測試,打包....
常用的命令:
- mvn clean : 清理
- mvn compile : 編譯主程序
- mvn test-compile : 編譯測試程序
- mvn test : 執行測試
- mvn package : 打包
- mvn install : 安裝
- mvn site :生成站點
Maven命令會有關聯網問題:
- Maven 的核心程序中僅僅定義了抽象的生命週期,但是具體的工作必須有特定的插件來完成。而插件本身不包含在Maven核心程序中。
- 當我們執行的Maven命令需要用到某些插件時,Maven核心程序會首先到本地倉庫中查找。
- 本地倉庫的默認位置:[系統登陸用戶的家目錄] \ .m2\repository
- Maven核心程序如果在本地倉庫中找不到需要的插件,那麼它會自動連接外網,到中央倉庫下載。
- 如果此時無法連接外網,則構建失敗。
- 修改默認本地倉庫的位置可以讓Maven核心程序到我們事先準備好的目錄下查找插件
①找到Maven解壓目錄\conf\settings.xml
②在setting.xml 文件中找到 localRepository 標籤
③將 < localRepository>/path/to/local/repo< /localRepository>從註釋中取出
④將標籤體內容修改爲自定義的Maven倉庫目錄
2.3 POM
Project Object Model:項目對象模型。將Java工程的相關信息封裝爲對象作爲便於操作和管理的模型。
pom.xml 對於 Maven工程是核心配置文件,與構建過程相關的一切設置都在這個文件中進行配置。可以說學習Maven就是學習pom。xml文件中的配置。
2.4 座標
爲各種使用下面三個向量在倉庫中唯一定位一個Maven工程:
(1)groupid:公司或組織域名倒序+項目名
<groupid>/groupid>
(2)artifactid:模塊名
< artifactid>Hello< /artifactid>
(3)version:版本
< version>1.0.0< /version>
Maven 工程的座標與倉庫中路徑的對應關係,以spring爲例:
< groupId>org.springframework< /groupId>
< artifactId>spring-core< /artifactId>
< version>4.0.0.RELEASE< /version>
org/springframework/spring-core/4.0.0.RELEASE/spring-core-4.0.0.RELEASE.jar
注意:我們自己的 Maven 工程必須執行安裝操作纔會進入倉庫。安裝的命令是:mvn install
2.5 倉庫
倉庫的分類:
(1)本地倉庫:當前電腦部署的倉庫目錄,爲當前電腦上的所有Maven工程服務
(2)遠程倉庫:
①私服:搭建在局域網環境中,爲局域網範圍內的所有Maven工程服務
②中央倉庫:假設在Internet上,爲全世界所有Maven工程服務
③中央倉庫鏡像:爲了分擔中央倉庫的流量,提升用戶訪問速度
倉庫中保存的內容:Maven工程
(1)Maven自身所需要的插件
(2)第三方框架或工具的jar包
(3)我們自己開發的Maven工程
不管是什麼樣的jar包,在倉庫中都是按照座標生成目錄結構,所以可以通過統一的方式查詢或依賴。
2.6 依賴
2.6.1 依賴實驗
我們分別創建兩個Maven項目,分別是Hello和HelloFriend。在HelloFriend的類中調用了Hello的類:
package com.lianxi.maven;
import com.lianxi.maven.Hello;
public class HelloFriend {
public String sayHelloToFriend(String name){
Hello hello = new Hello();
String str = hello.sayHello(name)+" I am "+this.getMyName();
System.out.println(str);
return str;
}
public String getMyName(){
return "John";
}
}
轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消轉存失敗重新上傳取消在HelloFriend的pom.xml配置文件中有對Hello的依賴:
<dependency>
<groupId>com.lianxi.maven</groupId>
<artifactId>Hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
嘗試對HelloFriend進行編譯,會發現編譯失敗。
當 A jar 包用到了 B jar 包中的某些類時,A 就對 B 產生了依賴,這是概念上的描述。Maven解析依賴信息時會到倉庫中查找被依賴的jar包。對於我們自己開發的Maven工程,要使用mvn install 命令安裝後就可以進入倉庫。
所以對Hello要先執行mvn install命令,然後再編譯HelloFriend,就可以執行成功了。
2.6.2 依賴的標籤
根元素project下的dependencies可以包含多個dependency元素,以聲明一個或多個項目依賴,每個依賴可以包含的元素有:
- groupId,artifactId和version:依賴的基本座標,對於任何依賴來說,基本座標最重要,maven根據座標才能找到對應的依賴;
- type:依賴的類型,對應項目座標定義的packaging,大部分情況下無須聲明,默認是jar;
- scope:依賴的範圍;
- optional:標記依賴是否可選;
- exclusions:用來排除傳遞性依賴;
2.6.3 依賴的範圍
首先需要知道,Maven在編譯項目主代碼的時候需要一套classpath。其次,Maven在編譯和執行測試的時候會使用另一套的classpath。最後,實際運行Maven項目的時候,又會使用一套classpath。
依賴範圍就是用來控制依賴與這三種classpath(編譯classpath,測試classpath,運行classpath)的關係,Maven有以下幾種依賴範圍:
- compile:編譯依賴範圍,若未指定依賴範圍,會默認該依賴範圍,使用該依賴範圍的maven依賴,對於編譯、測試、運行的classpath都有效;比較典型的就是spring-core,在編譯、測試和運行項目的時候都需要使用到該依賴;
- test:測試依賴範圍,使用該依賴範圍的maven依賴,只對測試的classpath有效;典型的就是Junit,它只有在編譯測試代碼的時候纔有效;
- provided:已提供依賴範圍,使用該依賴範圍的maven依賴,只對編譯和測試的classpath有效,但在運行時無效;典型的就是servlet-api,編譯和測試的時候需要該依賴,但在運行項目的時候,由於容器已經提供了,就不需要maven重複的引入一遍;
- runtime:運行時依賴範圍,使用此類範圍的maven依賴,對於測試和運行的classpath有效,但在編譯主代碼的時候無效。典型的例子是JDBC驅動的實現,項目住代碼只需要JDK提供JDBC的接口,只有在測試或者運行項目的時候才需要實現上述接口的具體實現;
- system:系統依賴範圍,使用此類範圍的maven依賴,也是對應編譯和測試的classpath有效,運行時無效,但是使用該範圍的依賴,必須顯式的通過systemPath指定依賴的路徑(因爲此類依賴不是通過maven倉庫解析的,而往往與本地機綁定,可移植性比較差,謹慎使用)
- import:導入依賴範圍。該依賴範圍不會對3種classpath產生實際的影響。
以下是一個簡單的表格(Y代表有效,N代表無效);
依賴範圍(scope) | 對於編譯classpath有效 | 對於測試classpath有效 |
對於運行classpath有效 |
典型例子 |
compile | Y | Y | Y | spring-core |
test | N | Y | N | Junit |
provided | Y | Y | N | servlet-api |
runtime | N | Y | Y | JDBC驅動實現 |
system | Y | Y | N | 本地的,Maven倉庫以外的類庫文件 |
2.6.4依賴的傳遞性
A依賴B,B依賴C,A能否使用C呢?那要看B依賴C的範圍是不是compile,如果是則可用,否則不可用。
Maven工程 | 依賴範圍 | 對A的可見性 | ||
A | B | C | compile | Y |
D | test | N | ||
E | provided | N |
2.6.5 依賴的排除
如果我們當前工程中引入了一個依賴是A,而A又依賴了B,那麼Maven會自動將A依賴的B引入當前工程,但是個別情況下B有可能是一個不穩定版本,或對當前工程有不良影響。這時我們可以在引入A的時候將B排除。
情景背景:
配置方式:使用了上面提到的exclusion元素
<dependency>
<groupId>com.atguigu.maven</groupId>
<artifactId>HelloFriend</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>jar</type>
<scope>compile</scope>
<exclusions>
< exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</ exclusion>
</exclusions>
</dependency>
2.6.6 依賴的原則
依賴的原則就是爲了解決jar包衝突。
- 路徑最短者優先
- 路徑相同時最先聲明者優先
這裏“聲明”的先後順序指的是 dependency 標籤配置的先後順序。
2.6.7 統一管理所依賴 .jar 包的版本
對同一個框架的一組 jar 包最好使用相同的版本。爲了方便升級框架,可以將 jar 包的版本信息統一提取出來。
(1)統一聲明版本號
<properties>
<自定義標籤>4.1.1.RELEASE</自定義標籤>
</properties>
(2)引用前面聲明的版本號
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${自定義標籤名稱}</version>
</dependency>
……
</dependencies>
(3)其他用法
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
2.7 生命週期
2.7.1 概述
Maven生命週期定義了各個構建環節的執行順序,有了這個順序,Maven就可以自動化的執行構建命令了。Maven聲明週期有以下特點:
- 各個構建環節執行的順序:不能打亂順序,必須按照既定的正確順序來執行
- Maven的核心程序中定義了抽象到的生命週期,生命週期中各個階段的具體任務是由插件來完成的。
- Maven核心程序爲了更好的實現自動化構建,按照這一特點執行生命週期中各個階段:不論現在要執行生命週期中的哪一個階段,都是從這個生命週期最初始的位置開始執行,比如我們運行mvn install的時候,代碼會編譯,測試,打包。
Maven有三套相互獨立的生命週期,分別是:
- Clean Lifecycle:在進行真正的構建之前進行一些清理工作
- Default Lifecycle:構建的核心部分,編譯,測試,打包,安裝,部署等等
- Site Lifecycle:生成項目報告,站點,發佈站點。
它們是相互獨立的,你可以僅僅調用clean來清理工作目錄,僅僅調用site來生成站點。當然你可以直接運行 mvc clean install site運行所有這三套生命週期。
每套生命週期都是由一組階段組成的,我們平時在命令行輸入的命令總是會對應於一個特定的階段。比如,運行mvn clean,這個clean是Clean生命週期中的一個階段,有了Clean生命週期,也會有clean階段。
2.7.2 Clean生命週期
Clean生命週期一共包含了三個階段:
- pre-clean 執行一些需要在clean之前完成的工作
- clean 移除所有上一次構建生成的文件
- post-clean 執行一些需要在clean之後立刻完成的工作
2.7.3 Default生命週期
Default生命週期是Maven生命週期中最重要的一個,絕大部分的工作都發生在這個生命週期中,現在,只解釋一些比較重要和常用的階段:
- validate
- generate-sources
- process-sources
- generate-resources
- process-resources 複製並處理資源文件,至目標目錄,準備打包。
- compile 編譯項目的源代碼。
- process-classes
- generate-test-sources
- process-test-sources
- generate-test-resources
- process-test-resources 複製並處理資源文件,至目標測試目錄。
- test-compile 編譯測試源代碼。
- process-test-classes
- test 使用合適的單元測試框架運行測試。這些測試代碼不會被打包或部署。
- prepare-package
- package 接受編譯好的代碼,打包成可發佈的格式,如 JAR。
- pre-integration-test
- integration-test
- post-integration-test
- verify
- install 將包安裝至本地倉庫,以讓其它項目依賴。
- deploy 將最終的包複製到遠程的倉庫,以讓其它開發人員與項目共享或部署到服務器上運行。
2.7.4 Site生命週期
- pre-site 執行一些需要在生成站點文檔之前完成的工作
- site 生成項目的站點文檔
- post-site 執行一些需要在生成站點文檔之後完成的工作,並且爲部署做準備
- site-deploy 將生成的站點文檔部署到特定的服務器上
2.7.5 插件和目標
- Maven的核心僅僅定義了抽象的生命週期,具體的任務都是交給插件來完成的。
- 每個插件都能實現多個功能,每個功能就是一個插件目標
- Maven的生命週期與插件目標相互綁定,以完成某個具體的構建任務。例如:compile就是插件maven-compiler-plugin的一個目標。
2.8 繼承
2.8.1 使用繼承的原因
現在有三個Maven項目,MakeFriends依賴Hello和HelloFriend,HelloFriend依賴Hello。由於非 compile 範圍的依賴信息是不能在“依賴鏈”中傳遞的,所以有需要的工程只能單獨配置。例如:
Hello | <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.0</version> <scope>test</scope> </dependency> |
HelloFriend | <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.0</version> <scope>test</scope> </dependency> |
MakeFriends | <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.0</version> <scope>test</scope> </dependency> |
此時如果項目需要將各個模塊的junit版本統一爲4.9,那麼到各個工程中手動修改無疑是非常不可取的。 使用繼承機制就可以將這樣的依賴信息統一提取到父工程模塊中進行統一管理。
2.8.2 解決辦法
將Junit依賴統一提取到“父”工程中,在子工程中聲明Junit依賴是不指定版本,以父工程中統一設定的爲準。同時也便於修改。
操作步驟如下:
(1)創建一個Maven工程作爲父工程。注意:打包方式爲pom。(創建父工程和創建一般的 Java 工程操作一致,唯一需要注意的是:打包方式處要設置爲 pom。)
(2)在子工程中聲明對父工程的引用
<parent>
<!-- 父工程座標 -->
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
<relativePath>從當前目錄到父項目的 pom.xml 文件的相對路徑</relativePath>
</parent>
(3)將子工程的座標中與父工程座標中重複的內容刪除(例如相同的groupId等)
(4)在父工程中統一管理Junit的依賴,將 Parent 項目中的 dependencies 標籤,用 dependencyManagement 標籤括起來
<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
(5)在子項目中重新指定需要的依賴,刪除範圍和版本號
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
2.9 聚合
2.9.1 爲什麼要使用聚合
將多個工程拆分爲模塊後,需要手動逐個安裝到倉庫後依賴才能夠生效。修改源碼後也需要逐個手動進 行 clean 操作。而使用了聚合之後就可以批量進行 Maven 工程的安裝、清理工作。
2.9.2 如何配置聚合
在總的聚合工程中使用 modules/module 標籤組合,指定模塊工程的相對路徑即可。
<modules>
<module>../Hello</module>
<module>../HelloFriend</module>
<module>../MakeFriends</module>
</modules>
注意,這個甚至打亂順序都可以執行成功
三.在Eclipse使用Maven
-
Maven插件Eclipse已經內置。
-
Maven插件的設置: Window->Preferences->Maven
①installations : 指定Maven核心程序的位置。默認是插件自帶的Maven程序,改爲我們自己解壓的那個。
②user settings : 指定Maven核心程序中 conf/settings.xml 文件的位置,進而獲取本地倉庫的位置。 -
基本操作
(1)創建Maven版的Java工程
① 創建時勾選上 Create a simple project(skip archetype selection)
創建的Maven工程默認使用的是JDK1.5,打開Maven核心程序settings.xml文件,找到profiles標籤,加入如下配置,即可更改(或者在這個工程下Build Path修改)
<profile>
<id>jdk-1.7</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.7</jdk>
</activation>
<properties>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.compilerVersion>1.7</maven.compiler.compilerVersion>
</properties>
</profile>
(2)創建Maven版的Web工程
① 創建時勾選上 Create a simple project(skip archetype selection)
② Packing選擇war
③調整web目錄結構,在項目上點右鍵 -> properties->Project Facets -> 把Dynamic Web Module 勾選去掉,並Apply -> 將Dynamic Web Module 重新勾選 -> 點擊Further configuration available -> 修改 Content directory爲src/main/webapp -> Apply 即在Maven工程上生成動態Web目錄結構。目錄結構如下:
④新建jsp文件發現報錯:The superclass “javax.servlet.http.HttpServlet” was not found on the Java Build Path/。因爲缺少Tomcat 運行時環境,有兩種解決方法:
a.以Maven方式添加:在pom.xml文件中添加
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
b.將Tomcat配置的Server直接引入這個工程。
⑤jsp寫入EL表達式時發現報錯:javax.servlet.jsp cannot be resolved to a type。解決方法:將JSPAPI導入,在pom.xml文件中添加
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1.3-b06</version>
<scope>provided</scope>
</dependency>
注意:scope一定要是provided,不然jar包衝突,運行時會報空指針異常
(3)執行Maven命令:選中pom.xml 右鍵 Run As->Maven build…->Goals->輸入 compile ->點擊 run
本篇文章是尚硅谷Maven的視頻的學習筆記,還參考了《Maven實戰》