(V)Maven依赖

依赖详解


...
 
   
     
     
     
     
     
     
     
         
         ...
     
   
   ...
 

groupId、artifactId、version依赖的基本坐标,对任何一个依赖来说坐标是最重要的,Maven根据坐标才能找到所需要的依赖


type是依赖的类型,对应项目坐标定义的packaging。大部分情况下该元素不必声明,默认为jar


scope依赖范围:
Maven在编译项目主代码的时候要用一套编译classpath,这个时候域范围为compile;在执行测试的时候会使用另外一套测试classpath,域范围为test;实际运行Maven项目的时候,又会使用一套运行classpath

  • compile:编译依赖范围。不指定默认使用该依赖范围,对编译、测试、运行三种classpath都有效。典型例子就是spring-core,在编译、测试、运行时都需要用到。
  • test :测试依赖范围。只对测试classpath有效。在编译主项目代码和运行项目时无法使用该类依赖。典型例子就是JUnit,只在编译测试代码和运行测试的时候才需要。
  • provided:以提供依赖范围。对于编译和测试classpath有效。典型例子:servlet-api,编译和测试项目时需要引入该依赖,运行项目时,容器已经提供,就不需要Maven重复引入一遍
  • runtime:运行时依赖。对于测试和运行classpath有效。典型例子:JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或运行项目时才需要实现上述接口的具体JDBC驱动。
  • system:系统依赖范围。和provided依赖范围完全一致。但是使用system依赖范围时必须要通过systemPath元素显式指定依赖文件的路径。此类依赖不是通过Maven仓库解析的,往往与本机系统绑定,可能造成构建的不可移植。systemPath元素可以引用环境变量,eg:

  javax.sql
  jdbc-stdext
  2.0
  system
  ${java.home}/lib/rt.jar

  • import:导入依赖范围。该依赖范围不会对三种classpath产生实际的影响(阅读至8.3.3节回来补充)

optional依赖是否可选。当前有四个项目的依赖关系A→B(项目A依赖于项目B),B→X(可选),B→Y(可选)。根据传递性依赖定义,若这三个依赖范围都是compile,那么X,Y也是A的compile范围传递性依赖。这里由于XY都是可选依赖,依赖就不会传递。即XY对A不会有任何影响。

  • 为什么需要可选依赖?项目B实现了两个特性,特性一依赖于X,特性二依赖于Y,而且这两个特性是互斥的,用户不会同时使用这两个特性。比如B时一个持久层隔离工具包,支持多种数据库(MySQL,Oracle等),在构建这个工具包时,需要这两种数据库的驱动程序,但在使用这个工具包时,只会依赖一种数据库。


    可选依赖.png
  • 不推荐使用可选依赖。
    使用可选依赖的原因时一个项目实现了多个特性。在面向对象设计中,一个单一职责性原型,即一个类应该只有一项职责,而不是糅合太多的功能,在Maven项目中同样适用。我们看个例子。

    4.0.0
    com.play.myMaven
    project-b
    1.0-SNAPSHOT

    
        
          mysql
          mysql-connector-java
          5.1.34
          true
        
        
          com.oracle
          ojdbc14
          10.2.0.1.0
          true
        
    

在这个例子中,它们只会对当前项目B产生影响,当其他项目依赖于B的时候,这两个依赖不会被传递。当项目A依赖于项目B时,如果是基于MySQL数据库,那么在项目A中需要显式的声明mysql-connector-java这一依赖:


    4.0.0
    com.play.myMaven
    project-a
    1.0-SNAPSHOT

    
        
          com.play.myMaven
          project-b
          1.0-SNAPSHOT
        
        
          mysql
          mysql-connector-java
          5.1.34
        
    

更好的做法是为MySQL/Oracle分别创建一个Maven项目,基于相同的groupId,分配不同的artifactId,在各自的POM中声明对应的JDBC驱动依赖,不使用可选依赖,用户根据需要选择使用对应artifactId。


exclusions用来排除传递性依赖。传递性依赖会隐式地引入很多依赖。对于一些隐式的不稳定版本依赖和需要替换的传递性依赖,都需要手动排除。例如


    4.0.0
    com.play.myMaven
    project-a
    1.0-SNAPSHOT

    
        
          com.play.myMaven
          project-b
          1.0-SNAPSHOT
          
            
                com.play.myMaven
                project-c
            
          
        
        
          com.play.myMaven
          project-c
          1.10
        
    

排除依赖.png

项目A对项目C版本1.10的传递依赖,同时排除了B的传递依赖项目C


依赖调解
例如:项目A有这样的依赖关系:A→B→C→X(1,0)、A→D→X(2,0),X是A的传递性依赖,但是两条依赖路径上有两个版本的X,哪个X会被Maven解析?
Maven依赖调解的第一原则:路径最近者优先。X(2,0)路径为2,X(1,0)路径为3,X(2,0)被解析。
但如果是:A→B→Y(1,0)、A→C→Y(2,0),Y(1,0)和Y(2,0)依赖长度相同时,怎么解析呢?
Maven依赖调解的第一原则:第一声明者优先。在POM中依赖声明的顺序决定了谁会被先解析使用。


归类依赖
例如很多关于Spring Framework的依赖,为了避免重复书写同一项目的不同模块的版本,同时可以方便查看个构件版本,我们使用Maven属性归类依赖。


    4.0.0
    com.play.myMaven
    project-a
    1.0-SNAPSHOT

    
      5.0.5.RELEASE
    

    
        
          org.springframework
          spring-core
          ${springframework.version}
        
        
          org.springframework
          spring-context
          ${springframework.version}
        
        
          org.springframework
          spring-context-support
          ${springframework.version}
        
    

通过在properties中定义一个xxx.version子元素,用${xxx.version}来引用这一属性值。


优化依赖
对依赖进行优化:去除多余的依赖,显式地声明某些必要依赖。
Maven可以自动解析所有项目的直接依赖和间接依赖,并根据规则正确判断每个依赖的范围,对于一些依赖冲突,也能进行调解,以确保任何一个构件只有唯一的版本在依赖中存在。在这些之后,最后得到的那些依赖被称为已解析依赖。查看当前项目的已解析依赖,可以运行mvn dependency:list

[INFO] The following files have been resolved:
[INFO]    org.springframework:spring-web:jar:4.3.7.RELEASE:compile
[INFO]    org.springframework.boot:spring-boot-test:jar:1.5.2.RELEASE:test
[INFO]    com.fasterxml.jackson.core:jackson-annotations:jar:2.8.0:compile
[INFO]    org.hamcrest:hamcrest-library:jar:1.3:test
[INFO]    org.mockito:mockito-core:jar:1.10.19:test
[INFO]    org.springframework:spring-test:jar:4.3.7.RELEASE:test
[INFO]    org.slf4j:jul-to-slf4j:jar:1.7.24:compile
[INFO]    org.assertj:assertj-core:jar:2.6.0:test
[INFO]    org.springframework:spring-expression:jar:4.3.7.RELEASE:compile
[INFO]    com.icegreen:greenmail:jar:1.3.1b:test
[INFO]    org.skyscreamer:jsonassert:jar:1.4.0:test
...
[INFO]    org.springframework.boot:spring-boot:jar:1.5.2.RELEASE:compile
[INFO]    net.minidev:accessors-smart:jar:1.1:test
[INFO]    org.springframework:spring-context-support:jar:4.3.7.RELEASE:compile
[INFO]    com.fasterxml:classmate:jar:1.3.3:compile
[INFO]    ch.qos.logback:logback-classic:jar:1.1.11:compile
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

想要看的更清晰,可以以依赖树的方式,看到某个依赖是通过哪条路径引入的。运行:mvn dependency:tree

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building parent 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.10:tree (default-cli) @ hello-maven ---
[INFO] com.play.myMaven:hello-maven:jar:1.0-SNAPSHOT
[INFO] +- org.springframework:spring-core:jar:5.0.5.RELEASE:compile
[INFO] |  \- org.springframework:spring-jcl:jar:5.0.5.RELEASE:compile
[INFO] +- org.springframework:spring-context:jar:5.0.5.RELEASE:compile
[INFO] |  +- org.springframework:spring-aop:jar:4.3.7.RELEASE:compile
[INFO] |  +- org.springframework:spring-beans:jar:4.3.7.RELEASE:compile
[INFO] |  \- org.springframework:spring-expression:jar:4.3.7.RELEASE:compile
[INFO] +- org.springframework:spring-context-support:jar:5.0.5.RELEASE:compile
[INFO] +- mysql:mysql-connector-java:jar:5.1.34:compile
[INFO] +- com.oracle:ojdbc14:jar:10.2.0.1.0:compile
[INFO] +- junit:junit:jar:4.11:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] +- javax.mail:mail:jar:1.4.1:compile
[INFO] |  \- javax.activation:activation:jar:1.1:compile
[INFO] +- com.icegreen:greenmail:jar:1.3.1b:test
[INFO] |  \- org.slf4j:slf4j-api:jar:1.7.24:compile
...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

除此,我们还可以通过mvn dependency:analyze分析当前项目的依赖

[INFO] --- maven-dependency-plugin:2.10:analyze (default-cli) @ hello-maven ---
[WARNING] Used undeclared dependencies found:
[WARNING]    org.springframework.boot:spring-boot:jar:1.5.2.RELEASE:compile
[WARNING]    org.springframework:spring-beans:jar:4.3.7.RELEASE:compile
[WARNING]    org.springframework.boot:spring-boot-autoconfigure:jar:1.5.2.RELEASE:compile
[WARNING] Unused declared dependencies found:
[WARNING]    org.springframework.boot:spring-boot-starter-test:jar:1.5.2.RELEASE:test
[WARNING]    org.springframework.boot:spring-boot-starter-web:jar:1.5.2.RELEASE:compile
[WARNING]    com.oracle:ojdbc14:jar:10.2.0.1.0:compile
[WARNING]    mysql:mysql-connector-java:jar:5.1.34:compile
[WARNING]    org.springframework.boot:spring-boot-configuration-processor:jar:1.5.2.RELEASE:compile
[WARNING]    org.springframework:spring-core:jar:5.0.5.RELEASE:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
  • Used undeclared dependencies found:被用到但未被声明的依赖。这种依赖存在潜在的风险。当升级直接依赖时,相关传递性依赖的版本也可能发生变化,这种变化不易察觉,有可能导致当前项目出错。例如接口的改变,当前项目的相关代码无法编译。因此,我们需要显式声明任何项目中直接用到的依赖
  • Unused declared dependencies found:未被使用的显式声明的依赖。对于这种依赖,我们不能简简单单的删除声明,因为dependency:analyze只会分析编译主代码和测试代码需要用到的依赖,一些执行测试和运行时需要的依赖是发现不了的。这个操作确实可以找到一些没用的依赖,但是需要小心测试

注:《Maven实战》学习笔记

你可能感兴趣的:((V)Maven依赖)