maven依赖管理
maven依赖说明
Maven的依赖管理十分强大,单个项目的依赖管理非常简单,但是,当应用由多个模块组成,并且应用有数十到数百个模块组成时,依赖管理变得非常困难。此时,maven可以保证高度的依赖控制和稳定
maven的项目依赖主要配置在项目pom.xml的dependencies
节点中。依赖是使用Maven坐标来定位的,因此,使用任何一个依赖之间,你都需要知道它的Maven坐标。
在maven组件搜索网站上,每个组件都有maven依赖的配置内容,只需要将内容复制到项目pom.xml中的dependencies
节点即可。
junit junit 4.12
完整的依赖节点包含以下节点:
groupId,必选,实际隶属项目
artifactId,必选,其中的模块
version必选,版本号
type可选,依赖类型,默认jar
scope可选,依赖作用域,默认compile
optional可选,标记依赖是否可选,默认false
exclusion可选,排除传递依赖性,默认空
上面的示例是编写文档时,junit的maven依赖。
依赖传递
依赖传递是maven2.0的新特征。用于避免手动发现和添加需要的组件的依赖组件,maven会自动添加。
这个新特殊通过从远程仓库读取项目依赖的工程文件实现,一般而言,组件的传递性依赖包括继承自父项目的依赖组件,以及组件依赖的组件,等等。
因为没有限制依赖关系的层次。所以会产生一个问题:循环依赖(后面会有解决办法)
因为依赖传递,包含库可能会快速增长,出于这个原因,一些特征将用于限制依赖关系。
依赖调解
当遇到一个组件的多个版本依赖时,通过依赖调解决定使用哪个版本的组件。 maven2.0只支持“就近原则”,即选择离依赖树中最近一个版本。当然,你也可以选择显示的在项目pom文件中指定一个版本。 在maven2.0.9版本及之后的版本中,如果两个版本的组件在项目依赖树的在同一深度,那么哪个依赖先声明,用哪个版本。
举例说明:
在项目A中,依赖B与C组件,B组件依赖D1.0与E1.0,而组件C依赖于F与E2.0,F依赖与D2.0。此时,产生了依赖冲突,即对组件D和组件E的依赖。此时,因为组件D1.0更靠近项目,所以使用组件D1.0,对于E组件,因为距离项目的距离相同,所以POM中组件B与C在谁先声明,就用谁的依赖版本。
如果在POM中显示的声明D与E的版本,则使用指定版本。
依赖管理
即允许开发者在遇到传递性依赖,直接指定组件版本。
依赖作用域
见下节
依赖排除
假设X组件依赖于Y组件,Y组件依赖于Z组件,在引用X时,可以使用exclusion
节点,排队项目对Z组件的引用。这常常用于解除依赖调解的“就近原则” ,使用离项目较远的版本。
可选依赖
假设组件Y依赖于组件Z,在组件Y中可以通过optional
节点将Z定义为可选依赖。X引用Y组件依赖时,Z组件将不参与依赖传递。X组件可以选择性的显示引用Z组件。
maven依赖作用域
scope节点表示依赖作用域。
依赖作用域是用来限制依赖关系的传递性,同时影响各类组件的作用域。
maven依赖有以下几种:
compile
此作用域这是默认的作用域,如果依赖没的指定作用域,则使用此作用域。此依赖作用域的依赖可用于项目的所有类路径。此外,此依赖作用域参与依赖传递。
provided
此作用域和compile很像,只是在运行时此依赖由JDK或者容器提供。举个例子,当构建一个J2EE web应用时,你就要将Servlet API和Java EE API的依赖作用域设置为provided,因为容器提供了这些类。此依赖作用域只作用于编译和测试的类路径。不参与依赖传递
runtime
此作用域表示依赖在编译时不需要,但是执行时需要,作用于运行和测试时,但是不在编译的类路径中。
test
此作用域表示依赖在项目一般使用中不需要,只在测试编译和测试执行期间使用。
system
此作用域与provided作用域相似,只是你必须提供显示包含的jar。组件必须可用,maven不会在repository里查找组件。
import(只在Maven 2.0.9及以后的版本中可用)
此作用域只用于pom依赖,主要是用来导入另一个POM中 dependency management 里声明的依赖仲裁。
每个不同的作用域以不同的方式影响依赖传递。如下表所示: 如果一个项目依赖设置为左列的作用域,本依赖的传递依赖的作用域为第一行的值,那么传递依赖作用于本项目的的作用域将取交叉单元格的值,未列出的作用域,将被忽略
compile | provided | runtime | test | |
compile | compile(*) | - | runtime | - |
provided | provided | - | provided | - |
runtime | runtime | - | runtime | - |
test | test | - | test | - |
system作用域
system作用域的依赖组件应该总是可用,maven用不会去仓库中查找,system作用域通常用于告诉 maven 由JDK与虚拟机提供的依赖。如果你将一个依赖范围设置成系统范围,你必须同时提供一个 systemPath 元素。注意该范围是不推荐使用的
经典的使用例子就是JDBC标准扩展,Java身份验证和授权服务(JAAS)。
... ... javax.sql jdbc-stdext 2.0 system ${java.home}/lib/rt.jar
如果你的组件由JDK的tools.jar提供,系统路径应如下定义:
... ... sun.jdk tools 1.5.0 system ${java.home}/../lib/tools.jar
可选依赖
使用可选依赖的场景:一个项目A依赖于B,但是引用A的项目可能并不需要B,此时,A项目对B项目的依赖就可以配置成可选依赖,如果引用A的项目需要使用B的功能,可以手动引用B的依赖。
为什么使用可选依赖? 1. 节约磁盘、内存等空间; 1. 避免license许可问题; 1. 避免类路径问题,等等。
... sample.ProjectA Project-A 1.0 compile true
配置可靠依赖只需要在dependency节点中添加true即可。
依赖排除
依赖排除的使用场景:当一个项目A依赖项目B,而项目B同时依赖项目C,如果项目A中因为各种原因不想引用项目C,在配置项目B的依赖时,可以排除对C的依赖。
... sample.ProjectA Project-A 1.0 compile sample.ProjectB Project-B
配置依赖排除时,只需要在节点dependency
添加exclusions
节点,在exclusions
节点中添加一个或者多个exclusion
即可。exclusion
节点包含groupId
与artifactId
节点,用于定义排队的组件。
多重依赖
Demo:
对于如下依赖结构:
Project-A -> Project-B -> Project-D -> Project-E -> Project-F -> Project C
A对于E相当于有多重依赖,如果想在项目A中排除对E的依赖,只需要在配置B的依赖中进行即可:
4.0.0 sample.ProjectA Project-A 1.0-SNAPSHOT jar ...sample.ProjectB Project-B 1.0-SNAPSHOT sample.ProjectE Project-E
maven处理循环依赖
循环依赖
如果工程A依赖工程B,工程B又依赖工程A,就会形成循环依赖。或者A依赖B,B依赖C,C依赖A,也是循环依赖,总的来说,在画出工程依赖图之后,如果发现工程间的依赖连线形成了一个有向循环图,则说明有循环依赖的现象。
解决循环依赖
第一个办法是用build-helper-maven-plugin插件来规避。比如A依赖B,B依赖C,C依赖A的情况。这个插件提供了一种规避措施,即临时地将工程A、B、C合并成一个中间工程,编译出临时的模块D。然后A、B、C再分别依赖临时模块D进行编译
第二个办法是通过重构
重构的思路
第一个思路是平移重构,例如项目A和B互相依赖,那么可以将B依赖A的那部分平移到工程B中,或者将A依赖B的部分平移到A项目中,这样就能消除循环依赖。 第二个思路应对如下情况,比如A和B互相依赖,同时它们都依赖C,那么可以将B和A相互依赖的那部分移动到工程C里,这样一来,A和B相互之间都不依赖,只继续依赖C,也可以消除循环依赖。