maven系列(四)坐標與依賴


坐標和依賴

坐標詳解

Maven坐標為各種構件(artifact)引入了秩序,每一個artifact都必須明確定義自己的坐標,而一組Maven坐標是由以下元素定義的:

    <groupId>com.ssozh.mirana</groupId>
    <artifactId>mirana-parent</artifactId>
    <packaging>pom</packaging>
    <version>1.0.0-SNAPSHOT</version>
  • groupId:定義當前maven項目隸屬於的實際項目。
    • maven的項目和實際項目不一定是一對一關系,比如SpringFramework這一實際項目,對應的maven項目會有很多包括spring-core、spring-context等等。
    • groupId不應該對應項目隸屬的組織和公司,因為一個組織下面會有很多實際項目,如果groupId只定義到組織級別。那么artifactId會非常難以定義
    • groupId的表示方式與java包名的表示方式類似,通過與域名反向一一對應。上面的com.ssozh是我,mirana是我定義的實際項目。該groupId應該遇mirana.ssozh.com對應。
  • artifactId:該元素定義實際項目中的一個maven項目。
    • 推薦的做法是使用實際項目名稱作為artifactId的前綴,比如上面的離職中artifactId是mirana-parent。這樣做的好處是方便尋找實際構建。
    • 默認情況下,maven生成的構建,其文件名會議artifactId作為開頭,如mirana-parent-1.0.0-SNAPSHOT.jar。這樣就可以方便從一個lib文件夾中找到某個項目的一組構建。考慮有4個項目,每個項目都有core模塊,如果沒有前綴就會看到很多core-1.2.jar這樣的文件。
  • version:該元素定義maven項目當前所處的版本,如上例中的版本是快照版本。=>13章會詳解。
  • packaging:定義maven項目的打包方式。默認使用jar
  • classifier:該元素用來幫助定義構建輸出的一些附屬構件。
    • 附屬構件與主構件對應,比如javadoc和sources就是一些附屬構件,其包含了java文檔和源代碼。
    • 注意:不能直接定義項目的classifier,因為附屬構件倍速項目直接默認生成的,而是有附加的插件幫助完成的。

最后,項目構建的文件名是與坐標相對應的,規則為arifactId-version[-classifier].packaging

項目(略)

【因為】本項目不打算直接完成書中的項目,因此這個地方也是先看一遍,等自己的東西做好了,再過來補充這個部分。計划包括:

  1. OSS:可能不應該這么分,因為OSS無非是一種第三方調用而已。
  2. common:log4j
  3. admin:包含controller層的項目。

項目pom

項目的主代碼

項目的測試代碼

構建項目

使用mvn clean install構建項目,Maven會根據POM配置自動下載鎖所需要的依賴構件,執行編譯、測試、打包等工作。最后將項目生成的構件 安裝到本地倉庫中。這時,該模塊就能供其他Maven項目使用了。

依賴的配置

其實一個依賴聲明可以包含如下一些元素:

<project>
...
    <dependencies>
    	<dependency>
        	<groupId>...</groupId>
            <artifactId>...</artifactId>
            <version>...</version>
            <type>...</type>
            <scope>...</scope>
            <optional>...</optional>
            <exclusions>
            	<exclusion>
                	...
                </exclusion>
            </exclusions>
        </dependency>
		....
    </dependencies>
    ....
</project>

根元素project下的dependencies可以包含一個或多個dependency元素,以聲明一個或者多個項目依賴。每個依賴可以包含的元素有:

  • GAV:坐標。
  • type:依賴的類型,對應於項目坐標定義的packaging。大部分情況下該元素省略,默認jar
  • scope:坐標的范圍,見下文。
  • optional:標記依賴是否可選,見下文
  • exclusions:用來排除傳遞性依賴,見下文。

依賴范圍(可感知問題)

JUnit以依賴的范圍是test,測試范圍用元素scope表示。本節將詳細講解什么是測試范圍,以及各種測試范圍的效果和用途。

首先需要知道,Maven在編譯項目主代碼的時候需要使用一套classpath。比如編譯項目主代碼的時候需要用到spring-core,該文件以依賴的方式被引入到classpath中。
其次,maven在編譯和執行測試的時候會使用另外一套classpath。比如JUnit也會議依賴的方式引入到測試使用的classpath中,不同的是這里的依賴范圍是test。

最后,實際運行maven項目的時候,又會使用一套classpath,上例中的spring-core需要在該classpath中,而JUnit則不需要。

依賴范圍就是用來控制起來與這三種classpath(編譯classpath、測試classpath、運行classpath)的關系。Maven有以下幾種依賴范圍:

  • compile:【默認】編譯依賴范圍。使用此依賴范圍的maven依賴,對於編譯、測試、運行三種classpath都有效。

  • test:測試依賴范圍。使用此依賴范圍的maven依賴,只對測試classpath有效,在編譯主代碼或者運行醒目的使用時將無法使用此類依賴。

  • provided:已提供依賴范圍。使用此依賴范圍的maven依賴,對於編譯和測試classpath有效,但在運行時無效。典型的例子是servlet-api,編譯和測試項目的時候需要該依賴,但在運行項目的時候,由於容器已經提供,就不想要maven重復地引入一遍。

  • runtime:運行時依賴范圍。使用此依賴范圍的maven依賴,對於測試和運行classpath有效,但在編譯主代碼時無效。典型例子是JDBC驅動實現,項目主代碼的編譯只需要JDK提供的JDBC接口,只有在執行測試或者運行項目的時候才需要實現上述接口的具體JDBC驅動。

  • system:系統以來范圍。該依賴於三種classpath的關系,和provided依賴范圍完全能一致,但是使用system范圍的依賴時必須通過systemPath元素顯式地指定依賴文件的路徑。由於此類依賴不是通過maven倉庫解析的,而且常常與本機系統綁定,可能造成構建的不可移植,另外systemPath元素可以引用環境變量,如:

    <dependency>
    	<GAV></GAV>
        <scope>system</scope>
        <systemPath>${java.home}/lib/rt.jar</systemPath>
    </dependency>
    
  • import:導入依賴范圍。該依賴范圍不會對三種classpath產生實際的影響,該范圍依賴只在dependencyManagement元素下才有效果,使用該范圍的依賴通常指向一個pom,作用是將目標pom中的dendencyManagement配置導入並合並到當前POM的dependencyManagement元素中。

    • 注意,上述代碼中的依賴的type為pom,import范圍依賴由於其特殊性,一般都指向打包類型的pom的模塊。如果有多個項目,它們使用的依賴版本都是一致的,則就可以定義一個dependencyManagement專門管理依賴的POM,然后在各個項目中導入這些依賴管理配置。

傳遞性依賴

何為傳遞性依賴

【問題】:

考慮一個基於spring框架的項目,如果不使用maven則需要手動下載相關依賴,這么做會引入很多不必要的依賴。

另外一個做法就是懶加載,下載一個只有spring框架的jar包,到實際使用的時候,再根據出錯信息,或者查詢相關文檔,加入需要的其他依賴。

【解決方法】:mavn的傳遞性依賴機制。

有了傳遞性依賴機制,在使用spring的時候就不用考慮它依賴了什么,也不用擔心引入多余的依賴。maven會解析各個直接依賴的POM,將那些必要的間接依賴,以傳遞性依賴的形式傳遞到當前的項目中。

依賴性依賴和依賴范圍

假設A依賴於B,B依賴於C,我們說A對於B是第一直接依賴,B對C是第二個直接依賴,A對C就是傳遞性依賴。第一直接依賴和第二直接依賴的范圍決定了傳遞性依賴的范圍。依賴范圍影響傳遞性依賴:

- compile test provided runtime
compile compile - - runtiom
test test - - test
provided provided - provided provided
runtime runtime - - runtime

第一數列是第一依賴,第一橫列是第二依賴。中間的表格是傳遞范圍。

很明顯可以發現,當第二依賴的范圍是compile的時候,傳遞性依賴的范圍與第一直接依賴范圍一致;當第二依賴范圍是test的時候,依賴不會得以傳遞。

依賴調解

maven引入的傳遞性依賴機制,讓我們不用考慮這些直接依賴引入什么傳遞性依賴。但有時候,當傳遞性依賴造成問題的時候,我們就需要清除地知道該傳遞性依賴是從哪條依賴路徑引入的。

Maven依賴調節(dependency mediation)的第一原則是:路徑最近者優先。

Maven定義了依賴調解的第二原則:第一聲明者優先。在路徑長度一樣情況下,在POM中依賴聲明的順序最靠前的那個依賴優勝。例如:

A->B->Y(1.0)

A->C->Y(2.0)

如果B的依賴聲明C之前,那么Y(1.0)就會被解析使用。

可選依賴

【場景】

假設有這樣一個依賴關系,項目A依賴於項目B,項目B依賴於項目X與項目Y,但是B對於X和Y的依賴都是可選依賴。如果都是compile依賴,那么根據傳遞性,X和Y對於A就是compile范圍的依賴。然而由於X、Y是可選依賴,依賴將不會得以傳遞。

【為什么會有可選依賴】

存在一個項目B實現了兩個特性,其中的特性一依賴於X,特性二依賴於Y,而且這兩個依賴是互斥的,用戶不能同時使用這兩個特性。比如B是一個持久層隔離工具包,他支持多種數據庫,如Myql和postgreSQL等,在構建這個工具包的時候,需要這兩個數據可的驅動程序,但在使用的時候,只會依賴一種數據庫。因為出現了可選依賴

【使用方法】

<project>
	<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.10</version>
            <optional>true</optional>        
        </dependency>
                <dependency>
            <groupId>postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>8.4-701.jdbc3</version>
            <optional>true</optional>        
        </dependency>
    </dependencies>
</project>

上述XML中,使用<optional>表示這兩個依賴為可選依賴,他們只會對當前項目B產生影響,當其他項目依賴於B的時候,這兩個依賴不會被傳遞。因此當A依賴於B的時候,如果實際使用基於Mysql數據庫,那么再項目A中就需要顯示地聲明mysql-connector-java這一依賴。

在理想情況下,是不應該使用可選依賴的。在面向對象設計中,有一個單一職責性原則。這個規則在maven項目中也同樣使用。

最佳實踐

排除依賴

傳遞性依賴會給項目隱式地加入很多依賴,這極大的簡化了項目的管理,但是有些時候這種特性也會帶來問題。例如,當前項目有一個第三方依賴,而這個第三方依賴由於某些原因依賴了另外一個類庫的SNAPSHOT版本,那么這個SNAPSHOT就會成為當前的傳遞性依賴,而SNAPSHOT的不穩定性會直接影響到當前的項目。這個時候就需要排除掉該SNAPSHOT,並且在當前項目中聲明該類庫的某個正式發布的版本。具體用法就是用<exclusions>標簽就行了。

歸類依賴

對於同樣是來自於spring framework的不同模塊,比如spring-core,spring-beans等等。所有這些依賴的版本都是相同的,而且可以預見,如果將來要升級spring framework,這些依賴的版本會一起升級,對於這種情況應該使用<properties>元素定義maven屬性,定義一個springframework.version子元素,其值為2.5.6。然后通過${springframework.version}替換實際值。

優化依賴

【三個命令】

mvn dependency:list : 顯示已解析的依賴和每個依賴的范圍。

mvn dependency:tree:顯示依賴樹

mvn dependency:analyze

  1. used undeclared dependencies指項目中使用到的,但沒有顯式聲明的依賴。【當這個依賴升級了,可能導致當前項目出錯】
  2. Unused declared dependencies指的是項目中未使用的,但顯示聲明的依賴【需要注意的是,由於該命令只會分析編譯主代碼和測試代碼需要用到的依賴】

補充

classpath

maven項目分為src目錄,resource目錄,test/src目錄,test/resource目錄:

其中src和resource對應到項目的target\classes目錄,如果在src目錄調用classpath,則class的根目錄為target\classes

test/src,test/resource對應到test-classes目錄,如果在test/src目錄調用classpath,則class的根目錄為target\test-classes

maven的classpath就是java下面的地址,和resources下面的地址。因為最后都會編譯/復制到classes中去的。

其實也就是maven中說的主代碼所在的位置

這個地方相關的知識點可以查看:

  1. java.util.Properties
  2. org.springframework.util.ResourceUtils


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM