了解可选依赖和排除依赖的功能,能够帮助我们更好的理解依赖是什么、怎样使用、如何工作和何时最适宜应用。其中,排除依赖是作为依赖的基本概念而不是处于pom层。

一、    可选依赖

       当一个项目不适合分割成多个子模块的时候,我们可以使用可选依赖。它的思想在于某些依赖只应用于某些功能,而且当没有该功能时也不存在该依赖。理想状况下,一个功能可能被划分为一个子模块,该子模块是一个只有核心功能的项目,由于当你要使用这个子工程的功能的时候,你会需要它的全部,所以这个子工程只含有非可选性依赖。

      然而,如果工程不可被分割,这些依赖被声明为可选。如果一个用户想要使用和可选性依赖相关的功能,他们必须要在工程中重新声明可选性依赖。也许可选依赖和排除依赖不是解决问题的最好方法,但是不失为一种有效的解决方案。

      1. 为何使用可选依赖

      声明可选依赖不仅对于节省空间/内存等是重要的,而且对于使用一个工程的时候控制实际依赖的列表也是非常重要的。因为jar包最终可能编译成WAR、EAR、EJB等等,包含错误的jar包可能产生违反许可证协议、导致类路径错误等问题。

      2.如何使用optional标记

      在你的依赖声明中简单地将<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>
 
       3.可选依赖是如何工作的?
 
Project-A -> Project-B
 
       如上图所以projectA依赖projectB,当A在它的pom中声明B作为它的可选依赖时,这个关系会一直保持。 这就像在通常的构建中,projectB会被加到它的类路径中。
 
Project-X -> Project-A


      但是当另外一个工程projectX在它的pom中声明A作为其依赖时,可选性依赖这时就发挥作用了。你会发现projectB并不存在projectX的类路径中。如果X的类路径要包含B那么需要在你的pom中直接进行声明。

      4.例子:

      让我们假设现在有这样一个和Hibernate有类似功能的工程X2,支持多种诸如MySQL, postgre, oracle等数据库驱动/依赖,所有这些驱动/依赖是X2但不是你的工程所需要的,所以一种比较可行的方法就是X2将这些驱动/依赖声明为可选的。这样,无论任何时候当你的工程在pom中声明X2作为直接依赖,X2所支持的这些驱动/依赖都不会被自动加到你工程的类路径中,相反地,你必须要直接声明需要使用的数据库驱动/依赖。

二、  排除依赖

       由于maven2.x会传递解析依赖,所以很有可能一些你不需要的依赖也会包含在工程类路径中。例如,一些你依赖的工程可能没有正确的声明它们的依赖集。为了解决这种特殊情况,maven2.x已经引入了显式排除依赖的概念。排除是设置在pom中指定的依赖上,并且有指定的groupId和artifactId来标注。当你构建工程时,该物件不会像解析加载依赖一样被加载到你工程的类路径中。

       1. 如何使用排除依赖

       我们在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>
 
        2.排除依赖是如何工作的并且在何时使用它(作为最后一种解决方法)
 
Project-A
 
   -> Project-B
 
        -> Project-D <! -- This dependency should be excluded -->
 
              -> Project-E
 
              -> Project-F
 
   -> Project C


       如上图所示,Project-A依赖Project-B和C,Project-B依赖Project-D,Project-D依赖Project-E和F,默认的Project-A的类路径会包含:


B, C, D, E, F


       如果由于我们知道Project-D的某些依赖在仓库中丢失,那么我们不想Project-D和它所有的依赖加载到Project-A的类路径中,而且也不想/不需要依赖Project-D的Project-B的功能。在这种情况下,Project-B的开发者会提供一个Project-D的 <optional>true</optional>依赖,如下:


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

      然而,并不能达到你想要的效果。作为最后一种解决方法你仍然可以选择将它在Project-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>


      如果我们将Project-A部署到一个仓库中,而且Project-X声明了一个普通依赖到Project-A。那么Project-D是不是依旧从类路径中排除了?


Project-X -> Project-A


       答案是yes。Project-A已经声明了它不需要Project-D,所以它不会作为Project-A的传递依赖而引入。那么考虑下图的Project-X依赖Project-Y,


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


       Project-Y依赖于Project-B,并且它有需要Project-D所支持的功能,所以不能在Project-D的依赖列表中声明排除。也可再提供一个额外的我们可以解析Project-E的仓库。在这种情况下,不能将Project-D全局排除,因为它是Project-Y的合法依赖。

      在另外一种场景中,如果我们不需要的依赖是Project-E而不是Project-D,我们如何排除它呢?如下图:


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


      排除会影响到在依赖图上所有它的声明点以后的部分。如果你想排除Project-E而不是Project-D,可以简单的将排除指向Project-E。但是你无法将排除作用到Project-D,因为你无法改变Project-D的pom,如果你想这样,你应该用选择依赖而不是排除,或者将Project-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>
 
  </dependencie>


      3.为什么排除是依赖的基本概念,而不是在pom层上?

       这主要是为了确保依赖图是可预测的,并且防止排除不该排除的依赖。如果你用到了这个最终的解决方法并且必须要把它放到排除中,你必须绝对清楚你讨厌的传递依赖中有哪些依赖会被引入。

 

 4 依赖归类

 如果我们项目中用到很多关于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升级的时候只需要修改一处即可。

Xml代码  

1. <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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
 4.   
<groupId>com.mycompany.app</groupId>
<artifactId>my-app-simple</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>my-app-simple</name>
<properties>
<springframework.version>2.5.6</springframework.version>
</properties>
 13.   
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
 21.   
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springframework.version}</version>
</dependency>
</dependencies>
33. </project>