Maven依赖调解、 可选依赖、 依赖冲突 排除、 归类依赖、优化依赖

Maven依赖调解

Maven引入的传递性依赖机制,一方面大大简化和方便了依赖声明,另一方面,大部分情况下我们只需要关心项目的直接依赖是什么,而不用考虑这些直接依赖会引入什么传递性依赖。但有时候,当传递性依赖造成问题的时候,我们就需要清楚的知道该传递性依赖是从哪条依赖路劲引入的。

例如,项目A有这样的依赖关系:A->B->C->X(1.0)、A->D->X(2.0),X是A的传递性依赖,但是两条依赖路径上有两个版本的X,那么哪个X会被Maven解析使用呢?两个版本都解析显然是不对的,因为会造成重复依赖,因此必须选择一个。Maven依赖调解的第一原则是:路劲最近者优先,该例中X(1.0)的路径长度为3,而X(2.0)的路径长度为2,因此X(2.0)会被解析使用。

依赖调节第一原则不能解决所有的问题,如果两条依赖路径的长度是一样的呢?那到底谁被解析使用呢。Maven的依赖调解的第二原则:第一声明者优先。在依赖路径相等的情况下,在POM中依赖声明的顺序决定了谁会先被解析使用,顺序前的先解析使用。

可选依赖

假设有这样一个依赖关系,项目A依赖于项目B,项目B依赖于项目X和Y,B对于X和Y的依赖都是可选依赖:A->B、B->X(可选)、B->Y(可选)。根据传递性依赖的定义,如果所有这三个依赖的范围都是compile,那么X、Y就是A的compile范围的传递性依赖。然而,由于这里X、Y是可选依赖,依赖将不会得以传递。换句话说,X、Y将不会对A有任何影响。

为什么要使用可选依赖这一特性呢?可能是项目B实现了两个特性,其中的特性一依赖于X,特性二依赖于Y,而且这两个特性是互斥的,用户不可能同时使用这两个特性,比如B是一个持久层隔离工具包,它支持多种数据库包括:MySQL,PostgreSQL等,在构建这个工具包的使用,需要这两种数据库的驱动程序,但是在使用这个工具包的时候,只会依赖一种数据库。 
这里写图片描述

可选依赖的配置

这里写图片描述

上述XML代码片段中,使用<optional>元素表示mysql-connect-java和postgresql这两个依赖为可选依赖,他们只会对当前项目B产生影响,当其他项目依赖于B的时候,这两个依赖不会被传递,因此,当项目A依赖于项目B的时候,如果其实际使用基于MySQL数据库,那么在项目A中就需要显示地声明mysql-connect-java这一依赖。 
这里写图片描述

在理想的情况下,是不应该使用可选依赖的,而是应该为MySQL和PostgreSQL分别创建一个Maven项目,基于同样的groupId分配不同的artifactId,在各自的POM中声明JDBC驱动依赖,而且不使用可选依赖,用户则需要根据选择使用依赖。由于传递性依赖的作用,就不需要在声明JDBC驱动依赖。


==========================================================


情景再现:

 项目A依赖于项目B,项目B依赖于项目C(v1), 项目A依赖于项目D,项目D依赖于项目E,项目E依赖于C(v2),

1、A--->B---->C(v1) ,    

2、A------>D---->E----->C(v2)

项目A隐形依赖了两个版本的C,那到底采用哪个版本呢?


分析:

依赖调解第一原则:路径优先,很明显,第一种路径深度是3,第二种路径深度是4,所以,maven会采用C(v1)

依赖调解第二原则:声明优先,假设路径深度相等,那么声明在前的会被引用。


让我们继续思考,假设C(v1)、C(v2)版本,项目都不用,而是用C(v3),那么我们就用到了排除,

如下httpcomponent、poi各自排除codec,然后声明项目中需要的codec版本,exclusion只需要声明groupid、artifactid,不需要声明version,因为根据“依赖调解”的原则,一个项目中不可能出现groupid、artifactid相等而version相等的多个依赖。

exclusion,只会排除所在依赖项目中的依赖;就是说,我在httpcomponent声明了排除codec,并不会排除掉poi引用codec,要想poi中也排除,那么在poi中也声明排除


[html] view plain copy
  1. <dependency>  
  2.             <groupId>org.apache.httpcomponents</groupId>  
  3.             <artifactId>httpclient</artifactId>  
  4.             <version>4.4.1</version>  
  5.             <exclusions>  
  6.                 <exclusion>  
  7.                      <groupId>commons-codec</groupId>  
  8.                       <artifactId>commons-codec</artifactId>  
  9.                 </exclusion>  
  10.             </exclusions>  
  11.         </dependency>  
  12. <dependency>  
  13.             <groupId>org.apache.poi</groupId>  
  14.             <artifactId>poi</artifactId>  
  15.             <version>3.9</version>  
  16.             <exclusions>  
  17.                 <exclusion>  
  18.                      <groupId>commons-codec</groupId>  
  19.                      <artifactId>commons-codec</artifactId>  
  20.                 </exclusion>  
  21.             </exclusions>  
  22.         </dependency>  
  23. <dependency>  
  24.             <groupId>commons-codec</groupId>  
  25.             <artifactId>commons-codec</artifactId>  
  26.             <version>1.10</version>  
  27.         </dependency>  

===============================================


归类依赖

关于Spring Framework的依赖有很多,他们来自同一个项目的不同模块。因此所有这些依赖的版本都是相同的,可以知道,如果将来想要升级Spring Framework的话,这些依赖版本也会一起升级。这一情况在java中也似曾相识,考虑代码如下:

public double c(double r){
    return 2 * 3.14 * r;
}

public double s(double r){
    return 3.14 * r * r;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这两个简单的方程式计算圆的周长和面积,有经验的程序员一眼就能看出问题,使用字面量3.14显然不合适,应该定义一个常量并在方法中使用,如下:

public final double PI = 3.14;

public double c(double r){
    return 2 * PI * r;
}

public double s(double r){
    return PI * r * r;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

使用常量不仅可以使代码变得更加简洁,更重要的是可以避免重复,只需要更改一处,降低了错误的概率。 
使用maven属性归类依赖:

<project>
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.juven.mvnbook.account</groupId>
  <artifactId>account-email</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <name>Account Email</name>


  <properties>
     <springframework.version>2.5.6</springframework.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${springframework.version}</version>
    </dependency>

     <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${springframework.version}</version>
    </dependency>

     <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${springframework.version}</version>
    </dependency>
  </dependencies>
</project>
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

这里简单使用到了Maven的属性,首先使用properties元素定义了Maven的属性,该例子中定义了一个springframework.version子元素,其值为2.5.6,有了这个属性之后,Maven运行的时候,会将POM中所有${springframework.version}替换成2.5.6,也就是说,可以使用${}的方式来引用Maven的属性,然后将所有Spring Framework依赖的版本值用这一属性引用。这个和在java中常量PI替换3.14是同样的道理,只是语法不同而已。

优化依赖

在软件开发的过程中,程序员会通过重构等方式不断地优化自己的代码,使其变得更简洁、更灵活。同理,程序员也应该能够对maven项目的依赖了然于胸,并对其优化,如去除多余的依赖,显式声明某些必要的依赖。 
maven会自动解析所有项目的直接依赖和传递性依赖,并且根据规则正确判断每个依赖的范围。对于一些依赖冲突,也能进行调解,以确保任何一个构件只能唯一的版本在依赖中存在。在这些工作之后,最后得到的那些依赖被称为已解析依赖。 
可以通过运行如下命令查看当前项目的已解析依赖:mvn dependency:list 
这里写图片描述
将直接在当前POM声明的依赖定义为顶层依赖,而这些顶层依赖的依赖则定义为第二层依赖,以此类推,有第三、第四层依赖。当这些依赖经过Maven解析之后,就会构成一个依赖树,通过这颗依赖树,就能够很清楚地看到某个依赖是通过哪条路径引入的。可以运行如下命令查看当前项目的依赖树:mvn dependency:tree 
这里写图片描述

使用mvn dependency:listmvn dependency:tree可以帮助我们详细了解项目中所有的依赖的具体详细,在此基础上,还有mvn dependency:analyze工具可以帮助分析当前项目的依赖 
这里写图片描述


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值