这节主要讨论了可选的依赖排除功能。这将帮助用户理解它们是什么,怎么使用它们,它们是怎么工作的,和使用它们的最好的方式。同样也解释了为什么排除是在每个依赖的基础上,而不是在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>可选的依赖工作原理
1Project-A -> Project-B上面的图意味着项目A依赖于项目B,当A在它的POM文件中把B声明为一个可选的依赖,他们的关系依然没有改变。仅仅就像一次正常的构建,在这次构建中,项目B将会被添加进classpath。
1Project-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>依赖排除的工作原理和何时使用它(作为最后一招)
1Project-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将包含:
1B, 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中排除吗?
1Project-X -> Project-A答案是yes,项目A已经声明了它不需要项目D,所以项目D不会被传递地带到项目A中。
现在,考虑依赖于项目Y的项目X,就像下面表示的一样:
1Project-X -> Project-Y 2 -> Project-B 3 -> Project-D 4 ...项目Y同样也有也依赖于项目B,并且它需要一些被项目D支持的特性。因此,不要在这个依赖列表里放置一个对项目D的排除。它或许会供给一个额外的仓库,在这个仓库里,我们可以依赖项目E。在这个例子里,项目D不是被全局排除是非常重要的,因为项目Y有一个合理的依赖。
有另外一种情况,如果我们不想要项目E,而不是项目D,该怎么去排除呢?看看下面的图:
1Project-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
相关资源:敏捷开发V1.0.pptx