简介
这节主要讨论了可选的依赖排除功能。这将帮助用户理解它们是什么,怎么使用它们,它们是怎么工作的,和使用它们的最好的方式。同样也解释了为什么排除是在每个依赖的基础上,而不是在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级别
这用来保证依赖图是可预测的,并从排除一个依赖保持继承效果。如果你采取了最后的手段并放置了一个排除,你应该能绝对的确认哪个依赖被带进了一个不想要的传递依赖。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/117109.html