1. 程式人生 > >Maven原理與實踐

Maven原理與實踐

          Maven原理與實踐

一.maven簡介

  1. 何為maven

Maven是一個採用純Java編寫的開源專案管理工具。Maven採用了一種被稱之為project object model (POM)概念來管理專案,所有的專案配置資訊都被定義在一個叫做POM.xml的檔案中,通過該檔案,Maven可以管理專案的整個宣告週期,包括編譯,構建,測試,釋出,報告等等。目前Apache下絕大多數專案都已經採用Maven進行管理。而Maven本身還支援多種外掛,可以方便更靈活的控制專案。

Maven是面向技術層面,針對Java開發專案管理工具,它提供了構建工具所提供功能的超集,除了構建功能之外,Maven還可以管理專案結構、管理依賴關係、生成報告、生成Web站點、有助於團隊成員之間的交流與協作。

1.2maven的優勢

  • 指導開發:提供了Java專案的最佳開發實踐,自由開發專案骨架而可自動生成專案結構。
  • 自動編譯:不僅僅只像Ant自動編譯,還包括測試,打包,釋出,文件生成,專案站點生成……
  • 依賴管理:Maven可以方便地管理應用程式依賴,例如第三方依賴、模型依賴
  • 無限擴充套件:外掛模式可以無限增強Maven功能,例如通過TomcatJetty
    外掛可以自由控制其伺服器。
  • 持續整合:鼓勵開發者積極提交程式碼,更早地發現程式錯誤,在並行開發中穩妥推進。
  • 開發協作:更簡單和諧的團隊協作

1.3maven名詞解釋

  • Project任何您想build的事物,Maven都可以認為它們是工程。這些工程被定義為工程物件模型(POM,Poject Object Model)。一個工程可以依賴其它的工程;一個工程也可以由多個子工程構成。
  • POMPOM(pom.xml)是Maven的核心檔案,它是指示Maven如何工作的元資料檔案,類似於Ant中的build.xml檔案。POM檔案位於每個工程的根目錄中。
  • GroupId
    groupId是一個工程的在全域性中唯一的識別符號,一般地,它就是工程名。groupId有利於使用一個完全的包名,將一個工程從其它有類似名稱的工程裡區別出來。
  • Artifactartifact 是工程將要產生或需要使用的檔案,它可以是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的主要元件如下圖所示:

1.2.1專案物件模型(POM)

專案物件模型(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)報告時尤其要這麼做。


清單 2. 專案管理部分

  • 一個Java構件的五大座標元素:
  • groupId:組ID
  • artifactId:實際專案的ID
  • version:版本
  • package:包型別,如JAR、EAR、POM…
  • classifier:分類,如二進位制包,源、文件

通過這種規則就可以定位到世界上任何一個構件

1.2.1.2專案相關性部分

我們專案中用到的jar包可以通過依賴的方式引入,構建專案的時候從Maven倉庫下載即可。


清單 3. 專案相關性部分

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 在站點文件生成時包含還是排除某些報告。

清單 4. 專案構建部分

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 關聯jdktomcat:

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.xmlok.

4.2:使用並配置環境

1.新建maven專案:

File-new-other-maven-maven project---直接下一步--filte 中輸入webapp(這裡代表建立web 專案):選擇第二個,如圖:

 

下一步後。如下圖:

Group ID 一般寫大專案名稱。Artifact ID 是子專案名稱。(我這裡分別寫IFLYTEKsms,點選finishMaven 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 包,通過groupIDartifactId 以及版本號可以唯一確定一個jar 包。這樣可以防止老式Web 專案中WEB-INF/lib jar 包不一致的問題。

並且maven 還會自動下載新增進的jar 包所依賴的jar 包。

 

Run asmaven 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,再稍等片刻,等待包的下載。