1. 程式人生 > >《Maven官方指南》可選的依賴和依賴排除

《Maven官方指南》可選的依賴和依賴排除

原文連結

簡介

這節主要討論了可選的依賴排除功能。這將幫助使用者理解它們是什麼,怎麼使用它們,它們是怎麼工作的,和使用它們的最好的方式。同樣也解釋了為什麼排除是在每個依賴的基礎上,而不是在POM級別。

可選的依賴

當不可能把一個專案分裂成子模組(不管什麼原因)的時候,我們可以使用可選的依賴。它的思想就是:在專案中一些依賴僅僅被某些功能使用,並且如果這個功能不被使用,這個依賴就不需要。理想情況下,根據核心的功能性專案,一個功能被分成子模組… 如果你決定使用子模組的功能,因為你必須需要他們的全部,所以新的子專案僅僅有不可選的依賴。

然而,因為這個專案不可能被分成子模組,所以這些依賴被宣告成可選的。如果一個使用者想要使用和一個可選的依賴相關的功能,他們將不得不在他們自己的專案中重新宣告可選的依賴。用這種方法處理這種情況不是的最好的方式,但是可選的依賴和依賴排除也是一個權宜的解決辦法。

為什麼使用可選的依賴?

宣告可選的依賴,重點不是為了節省空間/記憶體,因為這些jar最後可能被打進一個WAR、EAR、EJB等,重點是當一個使用者為了使用一個專案時來控制實際的依賴列表。包含了一個錯誤的jar可能會違反一個許可協議,引起環境變數問題等。

怎麼使用可選的標籤?

在你的依賴宣告中,通過簡單的設定<optional> 標籤為true,一個依賴就被宣告為可選的。一個簡單的示例:

<project>
  ...
  <dependencies>
    <!-- declare the dependency to be set as optional -->
    <dependency>
      <groupId>sample.ProjectA</groupId>
      <artifactId>Project-A</artifactId>
      <version>1.0</version>
      <scope>compile</scope>
      <optional>true</optional> <!-- value will be true or false only -->
    </dependency>
  </dependencies>
</project>

可選的依賴工作原理

Project-A -> Project-B

上面的圖意味著專案A依賴於專案B,當A在它的POM檔案中把B宣告為一個可選的依賴,他們的關係依然沒有改變。僅僅就像一次正常的構建,在這次構建中,專案B將會被新增進classpath。

Project-X -> Project-A

但是當一個其他的專案(專案X)在它的POM檔案中宣告專案A為一個依賴,這個可選的依賴就發揮作用了。你將會注意到專案X的classpath不會包含專案B:為了把B包含進專案X的classpath,你需要在你的POM檔案中直接宣告。

例子:

有一個名為X2的專案,這個專案和hibernate有一些類似的功能,支援許多資料庫驅動/依賴,比如說MySQL,postgre,oracle等。為了構建X2,所有的這些依賴都是必須的,但是對於你的專案來說卻不是必須的,所以對於X2把這些依賴宣告為可選的是非常實用的,不論什麼時候當你在POM檔案中把X2宣告為一個直接依賴的時候,所有被X2支援的驅動不會自動的被包含進你的專案的classpath,你需要直接宣告你將要使用的資料庫的依賴/驅動。

依賴排除

因為maven2.X的依賴是傳遞的,可能會把不想要的依賴包含進你的classpath。比如說 ,你所依賴的專案或許沒有正確的設定它們的依賴集。為了處理這種特殊的情況,maven2.x包含了依賴排除的概念。排除在你的POM設定了一個特殊的依賴,並目標到一個特殊的groupId和artifactId,當你構建專案的時候,通過宣告排除依賴,這個特殊的artifactId不會被新增到你的專案的classpath中。

怎麼使用依賴排除:

在pom檔案的<dependency>部分增加<exclusions> 標籤:

<project>
  ...
  <dependencies>
    <dependency>
      <groupId>sample.ProjectA</groupId>
      <artifactId>Project-A</artifactId>
      <version>1.0</version>
      <scope>compile</scope>
      <exclusions>
        <exclusion>  <!-- declare the exclusion here -->
          <groupId>sample.ProjectB</groupId>
          <artifactId>Project-B</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>
</project>


依賴排除的工作原理和何時使用它(作為最後一招)

Project-A
   -> Project-B
        -> Project-D <! -- This dependency should be excluded -->
              -> Project-E
              -> Project-F
   -> Project C

上面的圖表表示專案A依賴於整個專案B和專案C,專案B依賴於專案D,專案D依賴於專案E和F,預設情況下,專案A的classpath將包含:

B, C, D, E, F

因為我們知道專案D的一些依賴在倉庫中丟失了,所以,如果我們不想要把專案D和它的依賴新增到專案A的classpath中會怎樣呢?並且你不想要專案B中依賴於專案D的某些功能,在這種情況下,專案B的開發者在專案D的依賴上增加<optional> true</optional>標籤,就像下面這樣:

<dependency>
  <groupId>sample.ProjectD</groupId>
  <artifactId>ProjectD</artifactId>
  <version>1.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>

然而,他們並沒有這麼做。使用最後的手段,你仍然可以把它排除在外,在專案A中,像這樣:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>sample.ProjectA</groupId>
  <artifactId>Project-A</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
  ...
  <dependencies>
    <dependency>
      <groupId>sample.ProjectB</groupId>
      <artifactId>Project-B</artifactId>
      <version>1.0-SNAPSHOT</version>
      <exclusions>
        <exclusion>
          <groupId>sample.ProjectD</groupId> <!-- Exclude Project-D from Project-B -->
          <artifactId>Project-D</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>
</project>

如果我們把專案A部署到倉庫,並且專案X聲明瞭一個正常的依賴,依賴於專案A,專案D會被從classpath中排除嗎?

Project-X -> Project-A

答案是yes,專案A已經聲明瞭它不需要專案D,所以專案D不會被傳遞地帶到專案A中。

現在,考慮依賴於專案Y的專案X,就像下面表示的一樣:

Project-X -> Project-Y
               -> Project-B
                    -> Project-D
                       ...

專案Y同樣也有也依賴於專案B,並且它需要一些被專案D支援的特性。因此,不要在這個依賴列表裡放置一個對專案D的排除。它或許會供給一個額外的倉庫,在這個倉庫裡,我們可以依賴專案E。在這個例子裡,專案D不是被全域性排除是非常重要的,因為專案Y有一個合理的依賴。

有另外一種情況,如果我們不想要專案E,而不是專案D,該怎麼去排除呢?看看下面的圖:

Project-A
   -> Project-B
        -> Project-D
              -> Project-E <!-- Exclude this dependency -->
              -> Project-F
   -> Project C

整個依賴的排除工作在我們宣告的點下進行。如果你想要排除專案E,你只需要改變排除的點,但是你不能在專案D進行排除,你不能改變專案D的POM檔案,如果可以,就會使用可選的依賴而不是排除了,或者簡單的把專案D分成子模組,

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>sample.ProjectA</groupId>
  <artifactId>Project-A</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>
  ...
  <dependencies>
    <dependency>
      <groupId>sample.ProjectB</groupId>
      <artifactId>Project-B</artifactId>
      <version>1.0-SNAPSHOT</version>
      <exclusions>
        <exclusion>
          <groupId>sample.ProjectE</groupId> <!-- Exclude Project-E from Project-B -->
          <artifactId>Project-E</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>
</project>

為什麼排除是在每個依賴的基礎上,而不是在POM級別

這用來保證依賴圖是可預測的,並從排除一個依賴保持繼承效果。如果你採取了最後的手段並放置了一個排除,你應該能絕對的確認哪個依賴被帶進了一個不想要的傳遞依賴。