Maven原理與實踐
Maven原理與實踐
一.maven簡介
- 何為maven
Maven是一個採用純Java編寫的開源專案管理工具。Maven採用了一種被稱之為project object model (POM)概念來管理專案,所有的專案配置資訊都被定義在一個叫做POM.xml的檔案中,通過該檔案,Maven可以管理專案的整個宣告週期,包括編譯,構建,測試,釋出,報告等等。目前Apache下絕大多數專案都已經採用Maven進行管理。而Maven本身還支援多種外掛,可以方便更靈活的控制專案。
Maven是面向技術層面,針對Java開發專案管理工具,它提供了構建工具所提供功能的超集,除了構建功能之外,Maven還可以管理專案結構、管理依賴關係、生成報告、生成Web站點、有助於團隊成員之間的交流與協作。
1.2maven的優勢
- 指導開發:提供了Java專案的最佳開發實踐,自由開發專案骨架而可自動生成專案結構。
- 自動編譯:不僅僅只像Ant自動編譯,還包括測試,打包,釋出,文件生成,專案站點生成……
- 依賴管理:Maven可以方便地管理應用程式依賴,例如第三方依賴、模型依賴
- 無限擴充套件:外掛模式可以無限增強Maven功能,例如通過Tomcat、Jetty
- 持續整合:鼓勵開發者積極提交程式碼,更早地發現程式錯誤,在並行開發中穩妥推進。
- 開發協作:更簡單和諧的團隊協作
1.3maven名詞解釋
- Project:任何您想build的事物,Maven都可以認為它們是工程。這些工程被定義為工程物件模型(POM,Poject Object Model)。一個工程可以依賴其它的工程;一個工程也可以由多個子工程構成。
- POM:POM(pom.xml)是Maven的核心檔案,它是指示Maven如何工作的元資料檔案,類似於Ant中的build.xml檔案。POM檔案位於每個工程的根目錄中。
- GroupId
- Artifact:artifact 是工程將要產生或需要使用的檔案,它可以是jar檔案,原始檔,二進位制檔案,war檔案,甚至是pom檔案。每個artifact都由groupId和 artifactId組合的識別符號唯一識別。需要被使用(依賴)的artifact都要放在倉庫(見Repository)中,否則Maven無法找到 (識別)它們。
- Dependency:為了能夠build或執行,一個典型的Java工程會依賴其它的包。在Maven中,這些被依賴的包就被稱為dependency。dependency一般是其它工程的artifact。
- Plug-in:Maven是由外掛組織的,它的每一個功能都是由外掛提供的。外掛提供goal(類似於Ant中的target),並根據在POM中找到的元資料去完成工作。主要的Maven外掛要是由Java寫成的,但它也支援用Beanshell或Ant指令碼寫成的外掛。
- Repository:倉庫。
二.maven原理簡介
Maven的主要元件如下圖所示:
專案物件模型(Project Object Model,POM)描述專案的各個方面。儘管對於 POM 的物理表示沒有內在的限制,但 Maven 開發人員通常使用一個 XML 專案檔案(project.xml)。該 XML 檔案格式由位於 Maven 安裝目錄中的 XML 模式(maven-project.xsd)定義。
通常,pom.xml 檔案由三個主要部分組成:
- 專案管理部分包括專案的組織、開發人員名單、原始碼位置和錯誤跟蹤系統 URL 等資訊。
- 專案相關性部分包括關於專案相關性的資訊。當前 Maven 實現(1.0 beta 測試版 8)僅支援 JAR 檔案相關性。
- 專案構建和報告部分包含專案構建資訊(如原始碼目錄、單元測試用例目錄)和要在構建中生成的報告。
清單 1. 主 project.xml 框架
2.1.1專案管理部分
專案管理部分(如清單 2 所示)主要包括可選項。在此部分中指定開發人員名單(帶有正確的標識),當您希望獲得更改日誌(Change Log)報告和開發活動(Development Activity)報告時尤其要這麼做。
- 一個Java構件的五大座標元素:
- groupId:組ID
- artifactId:實際專案的ID
- version:版本
- package:包型別,如JAR、EAR、POM…
- classifier:分類,如二進位制包,源、文件
通過這種規則就可以定位到世界上任何一個構件
1.2.1.2專案相關性部分
我們專案中用到的jar包可以通過依賴的方式引入,構建專案的時候從Maven倉庫下載即可。
groupId |
告訴 Maven 資源庫內哪個子目錄中包含相關性檔案。 |
artifactId |
告訴 Maven 該構件的唯一標識。 |
version |
表示相關性的版本號。 |
jar |
(可選的)表示相關性的 JAR 檔案。在絕大多數情況下,可以從相關性的 <artifactId> 和 <version> 構造 JAR 檔案的名稱。 |
type |
(可選的)相關性的型別;如 jar 和分發版等。預設值是 jar。 |
url scope |
(可選的)相關性專案的 URL,在相關性是在因特網上找到的第三方庫時有用。 (可選的)依賴的範圍,下面會進行詳解 |
exclusions |
(可選的)用來排除傳遞性依賴,下面會進行詳解 |
1.依賴範圍
Maven有以下幾種依賴範圍:
compile: 編譯依賴範圍。如果沒有指定,就會預設使用該依賴範圍。使用此依賴範圍的Maven依賴,對於編譯、測試、執行三種classpath都有效。
test: 測試依賴範圍。使用此依賴範圍的Maven依賴,只對於測試classpath有效,在編譯主程式碼或者執行專案的使用時將無法使用此類依賴。典型的例子就是JUnit,它只有在編譯測試程式碼及執行測試的時候才需要。
provided: 已提供依賴範圍。使用此依賴範圍的Maven依賴,對於編譯和測試classpath有效,但在執行時無效。典型的例子是servlet-api,編譯和測試專案的時候需要該依賴,但在執行專案的時候,由於容器已經提供,就不需要Maven重複地引入一遍。
runtime: 執行時依賴範圍。使用此依賴範圍的Maven依賴,對於測試和執行classpath有效,但在編譯主程式碼時無效。典型的例子是JDBC驅動實現,專案主 程式碼的編譯只需要JDK提供的JDBC介面,只有在執行測試或者執行專案的時候才需要實現上述介面的具體JDBC驅動。
system: 系統依賴範圍。該依賴與三種classpath的關係,和provided依賴範圍完全一致。但是,使用system範圍依賴時必須通過 systemPath元素顯式地指定依賴檔案的路徑。由於此類依賴不是通過Maven倉庫解析的,而且往往與本機系統繫結,可能造成構建的不可移植,因此 應該謹慎使用。systemPath元素可以引用環境變數。
2. 傳遞性依賴
我們可以看到此專案引入依賴junit和spring-core,我們可以在Maven倉庫中查詢spring-core構件,如圖:
點選POM我們會看到該檔案包含了一個commons-logging依賴:
那麼該依賴會傳遞到當前專案中,這就是依賴的傳遞性,開啟專案檢視Maven dependencies:
3. 可選依賴
有時候我們不想讓依賴傳遞,那麼可配置該依賴為可選依賴,將元素optional設定為true即可,例如:
那麼依賴該專案的另一專案將不會得到此依賴的傳遞
4.依賴調解
第一原則:路徑最短者優先,A -> B -> C ->X(1.0), A -> D -> X(2.0),此時A只會依賴X(2.0)
第二原則:第一宣告者優先,當第一原則無法解決依賴衝突時,則按在pom中依賴宣告的順序決定
5. 排除依賴
當我們引入第三方jar包的時候,難免會引入傳遞性依賴,有些時候這是好事,然而有些時候我們不需要其中的一些傳遞性依賴
比如上例中的專案,我們不想引入傳遞性依賴commons-logging,我們可以使用exclusions元素宣告排除依 賴,exclusions可以包含一個或者多個exclusion子元素,因此可以排除一個或者多個傳遞性依賴。需要注意的是,宣告exclusions 的時候只需要groupId和artifactId,而不需要version元素,這是因為只需要groupId和artifactId就能唯一定位依賴 圖中的某個依賴。換句話說,Maven解析後的依賴中,不可能出現groupId和artifactId相同,但是version不同的兩個依賴。
如下是一個排除依賴的例子:
6. 依賴歸類
如果我們專案中用到很多關於Spring Framework的依賴,它們分別是org.springframework:spring-core:2.5.6, org.springframework:spring-beans:2.5.6,org.springframework:spring- context:2.5.6,它們都是來自同一專案的不同模組。因此,所有這些依賴的版本都是相同的,而且可以預見,如果將來需要升級Spring Framework,這些依賴的版本會一起升級。因此,我們應該在一個唯一的地方定義版本,並且在dependency宣告引用這一版本,這一在 Spring Framework升級的時候只需要修改一處即可。
1.2.1.3專案構建部分
專案構建和報告部分(如清單 4 所示)包含用於配置某些 Maven 外掛的重要構建和報告資訊。例如,可以配置 Maven 在站點文件生成時包含還是排除某些報告。
1.編譯原始碼: mvn compile
2. 編譯測試程式碼:mvn test-compile
3. 執行測試:mvn test
4. 產生site:mvn site
5. 打包:mvn package
6. 在本地Repository中安裝jar:mvn install
7. 清除產生的專案:mvn clean
8. 生成eclipse專案:mvn eclipse:eclipse
9. 生成idea專案:mvn idea:idea
10. 組合使用goal命令,如只打包不測試:mvn -Dtest package
11. 編譯測試的內容:mvn test-compile
12. 只打jar包: mvn jar:jar
13. 只測試而不編譯,也不測試編譯:mvn test -skipping compile -skipping test-compile
( -skipping 的靈活運用,當然也可以用於其他組合命令)
14. 清除eclipse的一些系統設定:mvn eclipse:clean
三.資源庫
在以前使用Ant的時候,我們會建立一個lib目錄在存放我們的jar包,比如專案所依賴的第三方包,每建立一個專案都要建立一個lib,不停的做copy工作,不僅是對於磁碟的浪費,而且也造成了版本管理上的麻煩。而且我們還需要通過提交到svn上來對lib進行管理,但是svn對於這種二進位制檔案的管理並不出色。
Maven倉庫的初衷就是為了解決這個問題,是所有常用的第三方包的集中營。這樣所有的Maven專案就可以從這個倉庫中獲取所需要的資源,Maven倉庫中對jar通過Group Id, Atifact Id, version 來管理,所以Maven專案可以很方便的進行依賴管理。你不需要自己來管理這個龐大的資源倉庫,當然你可以建立一個公司層面的倉庫管理器,這個我在這個章節的後面會介紹。
Maven 倉庫的兩個概念:本地倉庫和遠端倉庫
本地倉庫是遠端倉庫的一個緩衝和子集,當你構建Maven專案的時候,首先會從本地倉庫查詢資源,如果沒有,那麼Maven會從遠端倉庫下載到你本地倉庫。這樣在你下次使用的時候就不需要從遠端下載了。如果你所需要的jar包版本在本地倉庫沒有,而且也不存在於遠端倉庫,Maven在構建的時候會報錯,這種情況可能發生在有些jar包的新版本沒有在Maven倉庫中及時更新。
Maven預設的本地倉庫地址為${user.home}/.m2/repository 。也就是說,一個使用者會對應的擁有一個本地倉庫。當然你可以通過修改${user.home}/.m2/settings.xml 配置這個地址:
如果你想讓所有的使用者使用統一的配置那麼你可以修改Maven主目錄下的setting.xml:
${M2_HOME}/conf/setting.xml
Settings.xml配置介紹
localRepository: 自定義本地庫路徑,預設在$user.home/.m2中
interactiveMode:
offline:是否每次編譯都去查詢遠端中心庫
pluginGroups:外掛組,例如org.mortbay.jetty
proxies:通過代理訪問外部庫
servers:整合認證服務,例如整合Tomcat
mirrors:映象庫,可以指定內部中心庫
profiles:個性配置,需要在Activation標籤中啟用
activeProfiles:表示啟用的profile
在 POM 中配置遠端倉庫
下面我介紹下如何在pom.xml裡面配置遠端倉庫,我們需要在什麼時候配置遠端倉庫呢?當你連線中央倉庫的速度比較慢時,或者你為你的公司搭建了 自己的倉庫,比如Nexus倉庫管理(後面我會介紹),又或者你蘇需要的jar存在另外一個公共倉庫,比如我們配置一個國內的映象地址:
配置映象
如果你想覆蓋中央倉庫的預設地址,那麼這裡我們就會使用的映象了,還在settings.xml裡面配置:
內部中心倉庫也稱私有共享倉庫(私服)。一般是由公司自己設立的,只為本公司內部共享使用。它既可以作為公司內部構件協作和存檔,也可作為公用類庫映象快取,減少在外部訪問和下載的頻率。
Nexus和Artifactory均可搭建倉庫伺服器。但後者支援LDAP認證,這樣就可以將私有倉庫的認證整合到公司已經有的LDAP認證伺服器。
內部中心庫又可以連線第三方庫,例如Jboss中心庫、Spring中心庫,以隨時獲得最新版本的第三方構件。
四.Eclipse+tomcat+maven+Spring mvc實踐
4.1基礎環境(eclipse+tomcat+jdk+maven)
1.安裝jdk:
下載並安裝jdk,(過程比較簡單就略過了)。
配置環境變數:按照java 預設安裝路徑需要配置為:
JAVA_HOME: C:\Program Files\Java\jdk1.6.0_07 (注意JAVA_HOME 路徑後面不能有分號。)
path:%JAVA_HOME%\bin;
classpath: %JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;
在控制檯下輸入javac 有結果就表示安裝成功了。
2.安裝eclipse:
此次採用的是eclipse 4.2,代號Juno。(免安裝版,其他版本慎用,測試過helios版本的,照著這個文件配置會有錯誤)。
只要解壓到指定目錄即可。
3.安裝tomcat:
先安裝外掛:下載地址:http://www.eclipsetotale.com/tomcatPlugin.html#A3。我用的是tomcatPluginV33.zip。直接把外掛解壓放到eclipse 安裝目錄-plugins 目錄下。
再把tomcat 檔案放到c 盤根目錄(自選)。
4.關聯
eclipse 關聯jdk、tomcat:
在preferences-java- installed jres 中add- 在jre home 中選擇jdk 安裝目錄。(特別注意:不是jre 檔案,是jdk. 如果原來是jre 要換成jdk,如下圖)
在eclipse 的preferences-server-runtime environmen 中,add-選擇與之相關版本的tomcat(這裡選擇7.0),browse-選擇tomcat 根目錄,jre 選擇安裝過的jre。
再在tomcat 選項選擇你安裝的tomcat 版本,選擇tomcat 根目錄,ok。瀏覽器中輸入:http://localhost:8080/。就可以檢查是否成功安裝了tomcat。
5.安裝maven:
先安裝外掛:在eclipse 的marcketplace 輸入maven查詢,選擇maven integration for eclipse,按照嚮導安裝,重啟eclipse。在你的新建專案以及prefrences 就有maven這一項了。
再把maven 檔案解壓到c 盤根目錄(自選)。(maven 檔案請自行到網站下載,連結:http://www.apache.org/dyn/closer.cgi/maven/maven-3/3.0.4/binaries/apachemaven-3.0.4-bin.zip)
最後關聯操作:在preferences 的maven - user settings 中選擇maven檔案目錄目錄—conf 檔案-- settings.xml。ok.
4.2:使用並配置環境
1.新建maven專案:
File-new-other-maven-maven project---直接下一步--在filte 中輸入webapp(這裡代表建立web 專案):選擇第二個,如圖:
下一步後。如下圖:
Group ID 一般寫大專案名稱。Artifact ID 是子專案名稱。(我這裡分別寫IFLYTEK,sms),點選finish。Maven 的web 專案就建好了。
建立好的maven 專案:
2.構建Maven常規目錄結構。
在Maven 管理的專案中,檔案目錄一般都很固定,我們下面就建立一般的web maven 項
目目錄結構。構建並執行一個hello 的示例demo。
(1)建立source folder 資料夾:
到Workspace-專案名-src-main 下新建一個資料夾:java。同樣的在上級目錄src 目錄中新建test 資料夾,在test 下新建資料夾:java。在eclipse 右擊重新整理,目錄就出來了。(如果直接新建source folder 會出現問題)。
(2)新建spring 配置檔案:(mvc-dispatcher-servlet.xml)
在專案樹中找到src-main-webapp-WEB-INF,在這個目錄中新建一個資料夾命名為:
pages,用於以後存放jsp 檔案(這樣會讓專案看起來更清晰一點)。同時在WEB-INF 目
錄中新建檔案,命名為:mvc-dispatcher-servlet.xml
編輯mvc-dispatcher-servlet.xml檔案:
<beans xmlns ="http://www.springframework.org/schema/beans"
xmlns:context ="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="com.sms"/>
<mvc:annotation-driven/>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
其中<context:component-scan base-package="com"/>代表編譯時讀取的包(在classpath下新建的包,在文件下幾頁中我是新建com.sms.controller所以寫這個,掃描com目錄下的所有檔案),<mvc:annotation-driven />代表註釋驅動,<value>/WEB-INF/pages/</value>代表前端控制器尋找jsp 的路徑。
(3)編輯web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/webapp_
2_4.xsd" id="WebApp_ID" version="2.4">
<display-name>Spring Web MVC Application</display-name>
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
</ context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</ listener>
</web-app>
其中重要的配置是<context-param>中的<param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>,它指明瞭spring 配置檔案的目錄。(當然也可以用classpath:xxx的方式進行配置, 多個配置檔案也可以用“,”隔開)
<servlet-mapping>中的<url-pattern>/</url-pattern>,它代表前端攔截器將攔截所有檔案。
這裡要特別注意的是<servlet-name>:mvc-dispatcher,因為要對應<param-value>的/WEB-INF/mvc-dispatcher-servlet.xml。比如,如果<servlet-name>為mydispatcher,那麼對應的<param-value>必須為mydispatcher- servlet.xml。
(4)配置pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/mavenv4_
0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>IFLYTEK</groupId>
<artifactId>sms</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>sms Maven Webapp</name>
<url>http://maven.apache.org</ url>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>sms</finalName>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<version>1.1</version>
</plugin>
</plugins>
</build>
</project>
maven 通過pom.xml 管理專案依賴的jar 包,通過groupID、artifactId 以及版本號可以唯一確定一個jar 包。這樣可以防止老式Web 專案中WEB-INF/lib 下jar 包不一致的問題。
並且maven 還會自動下載新增進的jar 包所依賴的jar 包。
Run as—maven install,就會去自動下載包了。
至此,專案檔案結構如圖:
(5)Controller 類:
在src/main/java 下新建包:com.sms.controller,在這個包中新建controller
類:HelloController。編輯如下
packagecom.sms.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public classHelloController {
@RequestMapping("/hello.do")
public String index_jsp(ModelMap model){
System.out.println("hello");
model.addAttribute("yan", "yan你好");
return "hello";
}
}
(6)在pages 目錄下新建檔案hello.jsp,編輯如下:
<%@ page language="java" import="java.util.*" pageEncoding="utf-8" %>
<%
String path = request.getContextPath();
String basePath =
request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+ "/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'Login.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel=" stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
${yan}</br>
</body>
</html>
下載完畢後,啟動tomcat(在build 之前都要先啟動tomcat,不然會報build failed 錯誤)。在run as- maven build—雙擊新建一個maven build—在Goals 中輸入package tomcat:redeploy(關鍵)(如下圖):
點選剛才配置好的,點選選單欄上的綠色大按鈕run,再稍等片刻,等待包的下載。