Maven依赖总结

一:依赖范围

Maven在编译项目主代码的时候需要使用一套classpath。其次,在编译和执行测试的时候会使用另外一套classpath。最后,实际运行Maven项目的时候,又会使用一套classpath。

所谓的依赖范围就是用来控制依赖与这三种classpath(编译、测试、运行)的关系,Maven有以下几种依赖范围:

  • compile:编译依赖范围。如果没有指定,默认使用该依赖范围。使用此依赖范围时,对于编译、测试、运行都有效。例如:spring-core,编译、测试、运行时都需要使用该依赖。
  • test:测试依赖范围。只对测试classpath有效。例如:JUnit,它只在编译测试代码以及运行测试的时候才需要,编译和运行classpath时无法使用此依赖。
  • provided:已提供依赖范围。对于编译和测试时有效,但在运行时无效。例如:servlet-api,编译和测试项目的时候需要该依赖,但运行时,由于容器已经提供,就不需要Maven重复的引入。
  • runtime:运行时依赖。编译时无效,对于测试和运行有效。例如:JDBC驱动实现,编译时只需要JDK提供的JDBC接口,只有在执行测试和运行时才需要实现上述接口的具体JDBC驱动。
  • system:系统依赖范围。同provided。使用该依赖时必须通过systemPath元素显式地指定依赖文件路径。主要用于依赖本地的、且Maven仓库之外的类库文件。例如:
<dependency>
  <groupId>javax.sql</groupId>
  <artifactId>jdbc-stdext</artifactId>
  <version>2.5</version>
  <scope>system</scope>
  <systemPath>${spath}/lib/test.jar<systemPath>
</dependency>

二:传递依赖和依赖范围

当我们依赖一个a.jar时,如果a.jar依赖b.jar,那么只需要早pom中声明对a.jar的依赖即可,b.jar会被Maven自动加载进来。

例如:有一个org.springframework:spring-core:2.5.6的依赖,而实际上spring-core也有它自己的依赖,它依赖commons-logging。有了传递依赖机制,在使用spring-core时不需要考虑它依赖了什么。Maven会自动解析。

依赖范围在传递依赖时会略有变化

当第二直接依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致;

当第二直接依赖的范围是test的时候,依赖不会得以传递;

当第二直接依赖的范围是provided的时候,只传递第一直接依赖范围也为provi的依赖,且范围为provided;

当第二直接依赖的范围是runtime的时候,传递性的依赖范围与第一直接依赖的范围一致,但compile例外,此时传递依赖范围为runtime;

三:依赖调解

  • 原则一:路径最近者优先。例如:A ->B ->C ->X(1.0) 同时 A ->D ->X(2.0),很显然X(2.0)路径更短,会被解析使用。
  • 原则二:第一声明者优先。在依赖长度相等情况下,解析在pom中依赖声明中顺序考前的。例如:A ->B ->X(1.0) 同时 A ->D ->X(2.0)。如果B在D之前声明,那么X(1.0)会被解析。

除以上两种原则外,还可以手动排除,例如:A ->B ->X(1.0)同时A ->X(2.0)。如果项目A希望加载X(2.0)可做如下声明,通过<exclusion>元素来显式排除。

<dependency>
  <groupId>com.xxx.xx</groupId>
  <artifactId>xx-B</artifactId>
  <version>2.5</version>
  <exclusions>
    <exclusion>
      <groupId>com.xx</groupId>
      <artifactId>project-X</artifactId>
    </exclusion>
  </exclusions>
</dependency>
<dependency>
  <groupId>com.xxx.xx</groupId>
  <artifactId>project-x</artifactId>
  <version>2.0</version>
</dependency>

四:可选依赖

例如:b.jar是一个持久层工具包,它同时支持Mysql和PostgreSql,A项目依赖b.jar,那么在构建A时需要这两种数据库的驱动程序,但在使用的时候知会依赖一种数据库。A项目的依赖声明如下:

<dependency>
  <groupId>com.xxx.xx</groupId>
  <artifactId>xx.db</artifactId>
  <version>2.5</version>
  <dependencies>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.10</version>
      <optional>true</optional>
    </dependency>
    <dependency>
      <groupId>postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>8.4-701.jdbc3</version>
      <optional>true</optional>
    </dependency>
  </dependencies>
</dependency>

使用<optional>元素表示这两个为可选依赖,这是依赖不会传递到A项目,当A项目需要使用基于MySQL数据库时,需要显式声明对mysql的依赖。

另外:在理想的情况下是不会出现这种情况的,因为在面向对象设计中,有一个单一职责原则,即一个jar的职责应该只有一个。所以对于b.jar,最好是创建2个Maven项目,分别实现mysql和postgresql。

五:依赖优化

代码需要不断重构才能达到最优,依赖管理也是一样,需要不断的进行去除多余依赖,以及显式的声明某些必要的依赖。

Maven会自动解析所有项目的直接依赖和传递依赖,并根据规则判断每个依赖的范围,对于一些依赖冲突也能进行调节,这些工作之后得到这个项目的完整的已解析依赖。

可通过运行以下命令查看当前项目的已解析依赖:

mvn dependency:list

上图展示了当前项目中所有已解析的依赖,同时每个依赖的范围也得以明确标示。

如果将直接在pom中声明的依赖定义为第一层依赖,这些顶层依赖的依赖定义为第二层依赖,则以此类推可以形成一个完整的依赖树。

可运行以下明细查看当前项目的依赖树

mvn dependency:tree

从上图中可以清晰看出,虽然没有声明slf4j-api,但它通过传递依赖被加载进来,其范围为compile。

可运行以下命令对当前项目依赖进行简单分析

上图中Used undeclared dependencies,表示项目中使用到的,但是没有显式声明的依赖。可以看到第一个依赖是SNAPSHOT版本,它是通过传递依赖被加载进来的,这种依赖就是项目中的隐藏的、潜在的炸弹,因为引用的是SNAPSHOT非稳定版本,且不在pom中显式声明,很容易被忽略。该炸弹一旦爆炸,往往需要耗费大量时间来查明。

还有一个Unused declared dependencies,表示项目中未使用的,但是显式声明的依赖。如果真的不需要,建议去除声明。但需要注意的是,dependency:analyze知会分析编译和测试时需要用到的依赖,一些运行时依赖就无法被发现。所以在优化依赖时一定要小心测试。

时间: 2024-10-16 01:29:09

Maven依赖总结的相关文章

maven依赖本地非repository中的jar包-依赖jar包放在WEB-INF/lib等目录下的情况客户端编译出错的处理

maven依赖本地非repository中的jar包 http://www.cnblogs.com/piaolingxue/archive/2011/10/12/2208871.html 博客分类: MAVEN 今天在使用maven编译打包一个web应用的时候,碰到一个问题: 项目在开发是引入了依赖jar包,放在了WEB-INF/lib目录下,并通过buildpath中将web libariary导入. 在eclipse中开发没有问题,但是使用maven编译插件开始便宜总是报找不到WEB-INF

Java-Maven(七):Eclipse中Maven依赖、聚合、继承特性

之前通过学习了解,maven集成到eclipse中的如何创建项目,以及maven命令插件在eclipse中安装后的用法.那么接下来我们将会学习一些maven在项目中的一些特性,及如何使用. Maven依赖特性 Maven聚合特性 Maven继承特性

Maven依赖传递、依赖传递排除、依赖冲突

转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6628429.html  一:Maven依赖传递 假如有Maven项目A,项目B依赖A,项目C依赖B.那么我们可以说 C依赖A.也就是说,依赖的关系为:C->B->A. 那么我们执行项目C时,会自动把B.A都下载导入到C项目的jar包文件夹中. 这就是依赖的传递性. 二:依赖传递的排除 如上,C->B->A.加入现在不想执行C时把A下载进来,那么我们可以用 <exclusions>标

(转)Maven依赖的jar包下载不了、jar更新不了的解决办法

场景一: 使用Maven的同学可能偶尔会遇到这种情况:pom.xml中依赖了项目需要的某个jar文件,但是使用Maven –> update project 还是没办法下载该jar到项目中,你可能需要检查本地仓库的配置,打开.m2目录下的settings.xml,配置一个中央仓库(或者你使用的jar所在的仓库地址)的镜像: <mirror> <id>mvnrepositoryMID</id> <mirrorOf>mvnrepositoryRID<

Maven依赖版本冲突的分析及解决小结

1:前言 做软件开发这几年遇到了许多的问题,也总结了一些问题的解决之道,之后慢慢的再遇到的都是一些重复性的问题了,当然,还有一些自己没有完全弄明白的问题.如果做的事情是重复的,遇到重复性问题的概率也就会比较多了,如果是在一个新的领域里玩,遇到的问题又都是新的,自己从来没有见过的,但是问题的解决思路基本是类似的.下面这个问题,我觉得值得一记,因为以后还会再遇到类似的,我希望自己能很快的将其解决掉. 2:报错信息 如下是更新项目后,启动项目时抛出的部分错误信息. 十二月 14, 2016 7:52:

maven依赖传递关系

一.maven 依赖传递规则 举个例子,比如A依赖B,B依赖C,那么A也是依赖C的.A是对B的直接依赖,A对C是传递依赖 ①.最短路劲原则 如,路劲一:A依赖B,B依赖C,C依赖D(1.0.0): 路劲二:A依赖E,E依赖D(2.0.0), 此时A间接依赖D版本是2.0.0! ②.最先定义原则 如,路劲一:A依赖B,B依赖C(1.0.0): 路劲二:A依赖E,E依赖C(2.0.0), 此时A间接依赖C版本是1.0.0! 题外话,如果想精确的控制依赖包版本,可以使用依赖的排除功能——>exclus

Maven依赖范围

先看看Maven依赖的配置 <dependencies> <dependency> <groupId>....</groupId> <artifactId>......</artifactId> <version>........</version> <type>......</type> <scope>....</scope> <optional>.

Spring Security 3.2.x与Spring 4.0.x的Maven依赖管理

原文链接: Spring Security with Maven原文日期: 2013年04月24日翻译日期: 2014年06月29日翻译人员: 铁锚 1. 概述 本文通过实例为您介绍怎样使用 Maven 管理 Spring Security 和 Spring 的依赖关系.最新的Spring Security公布版本号能够在 Maven Central仓库 中找到. 译者建议訪问MVNRespotory中org.springframework.security链接.本文是 使用Maven管理Spr

maven 学习---Maven依赖机制

在 Maven 依赖机制的帮助下自动下载所有必需的依赖库,并保持版本升级. 案例分析 让我们看一个案例研究,以了解它是如何工作的.假设你想使用 Log4j 作为项目的日志.这里你要做什么? 1.在传统方式 访问 http://logging.apache.org/log4j/ 下载 Log4 j的 jar 库 复制 jar 到项目类路径 手动将其包含到项目的依赖 所有的管理需要一切由自己做 如果有 Log4j 版本升级,则需要重复上述步骤一次. 2. 在Maven的方式 你需要知道 log4j

如何快速的解决Maven依赖冲突

为什么会出现依赖冲突 首先要说明Maven的依赖管理,具体的可以参考这边 Maven学习--依赖管理 这篇文章,maven在依赖冲管理中有一下几个原则. 依赖是使用Maven坐标来定位的,而Maven坐标主要由GAV(groupId, artifactId, version)构成.如果两个相同的依赖包,如果groupId, artifactId, version不同,那么maven也认为这两个是不同的. 依赖会传递,A依赖了B,B依赖了C,那么A的依赖中就会出现B和C. Maven对同一个gro