《Maven官方指南》可选的依赖和依赖排除

简介

这节主要讨论了可选的依赖排除功能。这将帮助用户理解它们是什么,怎么使用它们,它们是怎么工作的,和使用它们的最好的方式。同样也解释了为什么排除是在每个依赖的基础上,而不是在POM级别。

可选的依赖

当不可能把一个项目分裂成子模块(不管什么原因)的时候,我们可以使用可选的依赖。它的思想就是:在项目中一些依赖仅仅被某些功能使用,并且如果这个功能不被使用,这个依赖就不需要。理想情况下,根据核心的功能性项目,一个功能被分成子模块… 如果你决定使用子模块的功能,因为你必须需要他们的全部,所以新的子项目仅仅有不可选的依赖。

然而,因为这个项目不可能被分成子模块,所以这些依赖被声明成可选的。如果一个用户想要使用和一个可选的依赖相关的功能,他们将不得不在他们自己的项目中重新声明可选的依赖。用这种方法处理这种情况不是的最好的方式,但是可选的依赖和依赖排除也是一个权宜的解决办法。

为什么使用可选的依赖?

声明可选的依赖,重点不是为了节省空间/内存,因为这些jar最后可能被打进一个WAR、EAR、EJB等,重点是当一个用户为了使用一个项目时来控制实际的依赖列表。包含了一个错误的jar可能会违反一个许可协议,引起环境变量问题等。

怎么使用可选的标签?

在你的依赖声明中,通过简单的设置<optional> 标签为true,一个依赖就被声明为可选的。一个简单的示例:

01 <project>
02   ...
03   <dependencies>
04     <!-- declare the dependency to be set as optional -->
05     <dependency>
06       <groupId>sample.ProjectA</groupId>
07       <artifactId>Project-A</artifactId>
08       <version>1.0</version>
09       <scope>compile</scope>
10       <optional>true</optional> <!-- value will be true or false only -->
11     </dependency>
12   </dependencies>
13 </project>

可选的依赖工作原理

1 Project-A -> Project-B

上面的图意味着项目A依赖于项目B,当A在它的POM文件中把B声明为一个可选的依赖,他们的关系依然没有改变。仅仅就像一次正常的构建,在这次构建中,项目B将会被添加进classpath。

1 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> 标签:

01 <project>
02   ...
03   <dependencies>
04     <dependency>
05       <groupId>sample.ProjectA</groupId>
06       <artifactId>Project-A</artifactId>
07       <version>1.0</version>
08       <scope>compile</scope>
09       <exclusions>
10         <exclusion>  <!-- declare the exclusion here -->
11           <groupId>sample.ProjectB</groupId>
12           <artifactId>Project-B</artifactId>
13         </exclusion>
14       </exclusions>
15     </dependency>
16   </dependencies>
17 </project>


依赖排除的工作原理和何时使用它(作为最后一招)

1 Project-A
2    -> Project-B
3         -> Project-D <! -- This dependency should be excluded -->
4               -> Project-E
5               -> Project-F
6    -> Project C

上面的图表表示项目A依赖于整个项目B和项目C,项目B依赖于项目D,项目D依赖于项目E和F,默认情况下,项目A的classpath将包含:

1 B, C, D, E, F

因为我们知道项目D的一些依赖在仓库中丢失了,所以,如果我们不想要把项目D和它的依赖添加到项目A的classpath中会怎样呢?并且你不想要项目B中依赖于项目D的某些功能,在这种情况下,项目B的开发者在项目D的依赖上增加<optional> true</optional>标签,就像下面这样:

1 <dependency>
2   <groupId>sample.ProjectD</groupId>
3   <artifactId>ProjectD</artifactId>
4   <version>1.0-SNAPSHOT</version>
5 <optional>true</optional>
6 </dependency>

然而,他们并没有这么做。使用最后的手段,你仍然可以把它排除在外,在项目A中,像这样:

01 <project>
02   <modelVersion>4.0.0</modelVersion>
03   <groupId>sample.ProjectA</groupId>
04   <artifactId>Project-A</artifactId>
05   <version>1.0-SNAPSHOT</version>
06   <packaging>jar</packaging>
07   ...
08   <dependencies>
09     <dependency>
10       <groupId>sample.ProjectB</groupId>
11       <artifactId>Project-B</artifactId>
12       <version>1.0-SNAPSHOT</version>
13       <exclusions>
14         <exclusion>
15           <groupId>sample.ProjectD</groupId> <!-- Exclude Project-D from Project-B -->
16           <artifactId>Project-D</artifactId>
17         </exclusion>
18       </exclusions>
19     </dependency>
20   </dependencies>
21 </project>

如果我们把项目A部署到仓库,并且项目X声明了一个正常的依赖,依赖于项目A,项目D会被从classpath中排除吗?

1 Project-X -> Project-A

答案是yes,项目A已经声明了它不需要项目D,所以项目D不会被传递地带到项目A中。

现在,考虑依赖于项目Y的项目X,就像下面表示的一样:

1 Project-X -> Project-Y
2                -> Project-B
3                     -> Project-D
4                        ...

项目Y同样也有也依赖于项目B,并且它需要一些被项目D支持的特性。因此,不要在这个依赖列表里放置一个对项目D的排除。它或许会供给一个额外的仓库,在这个仓库里,我们可以依赖项目E。在这个例子里,项目D不是被全局排除是非常重要的,因为项目Y有一个合理的依赖。

有另外一种情况,如果我们不想要项目E,而不是项目D,该怎么去排除呢?看看下面的图:

1 Project-A
2    -> Project-B
3         -> Project-D
4               -> Project-E <!-- Exclude this dependency -->
5               -> Project-F
6    -> Project C

整个依赖的排除工作在我们声明的点下进行。如果你想要排除项目E,你只需要改变排除的点,但是你不能在项目D进行排除,你不能改变项目D的POM文件,如果可以,就会使用可选的依赖而不是排除了,或者简单的把项目D分成子模块,

01 <project>
02   <modelVersion>4.0.0</modelVersion>
03   <groupId>sample.ProjectA</groupId>
04   <artifactId>Project-A</artifactId>
05   <version>1.0-SNAPSHOT</version>
06   <packaging>jar</packaging>
07   ...
08   <dependencies>
09     <dependency>
10       <groupId>sample.ProjectB</groupId>
11       <artifactId>Project-B</artifactId>
12       <version>1.0-SNAPSHOT</version>
13       <exclusions>
14         <exclusion>
15           <groupId>sample.ProjectE</groupId> <!-- Exclude Project-E from Project-B -->
16           <artifactId>Project-E</artifactId>
17         </exclusion>
18       </exclusions>
19     </dependency>
20   </dependencies>
21 </project>

为什么排除是在每个依赖的基础上,而不是在POM级别

这用来保证依赖图是可预测的,并从排除一个依赖保持继承效果。如果你采取了最后的手段并放置了一个排除,你应该能绝对的确认哪个依赖被带进了一个不想要的传递依赖。

转载自 并发编程网 - ifeve.com

上一篇:DotNetNuke出错:“Runat 属性必须具有值 Server(The Runat attribute must have the value Server Error)"


下一篇:Velocity官方指南-使用Velocity