theboyaply
theboyaply
发布于 2020-07-22 / 874 阅读
0
0

3-详解maven解决依赖问题

原文:

https://mp.weixin.qq.com/s/b3UOPBlzqQ3RCuznezlwMw

本章主要内容

  1. maven web 依赖示例
  2. maven 约定配置
  3. maven 中 pom 文件
  4. maven 坐标详解
  5. maven 依赖导入功能
  6. maven 依赖范围详解
  7. maven 依赖的传递
  8. maven 中依赖调解功能
  9. 可选依赖(optional 元素)的使用
  10. 排除依赖的使用

maven web 依赖示例(maven1)

在介绍本章知识点之前,先简单感受一下 maven 的作用。

使用 idea 创建一个 maven 项目,或者前往 https://start.spring.io | https://start.aliyun.io 下载一个 maven 项目,我们只为项目添加 Web 依赖:

<dependency>
   <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

以阿里云 [https://start.aliyun.io] 为例:

start-aliyun

填好信息之后点击下载,解压压缩包并使用 idea 打开:

maven-web1

新增一个 Controoler 类:

package com.theboyaply.maven1.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author theboyaply
 * @version V1.0
 * @Date 2020-4-23
 * @description
 */

@RestController
public class HelloWorld {

    @GetMapping("/hello")
    public String sayHello() {
        return "Hello World";
    }

}

然后在项目根目录下打开 cmd 使用 mvn spring-boot:run 命令启动程序:

D:\myResource\maven\maven1>mvn spring-boot:run

如果本地仓库没有相关 jar 包,会先去远程仓库下载,下载完成后会继续启动程序:

D:\myResource\maven\maven1>mvn spring-boot:run
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------< com.theboyaply:maven1 >------------------------
[INFO] Building maven1 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.2.6.RELEASE:run (default-cli) > test-compile @ maven1 >>>
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven1 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ maven1 ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to D:\myResource\maven\maven1\target\classes
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ maven1 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\myResource\maven\maven1\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ maven1 ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to D:\myResource\maven\maven1\target\test-classes
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.2.6.RELEASE:run (default-cli) < test-compile @ maven1 <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.2.6.RELEASE:run (default-cli) @ maven1 ---
Downloading from nexus-aliyun: http://maven.aliyun.com/nexus/content/groups/public/org/springframework/boot/spring-boot-loader-tools/2.2.6.RELEASE/spring-boot-
loader-tools-2.2.6.RELEASE.pom
Downloaded from nexus-aliyun: http://maven.aliyun.com/nexus/content/groups/public/org/springframework/boot/spring-boot-loader-tools/2.2.6.RELEASE/spring-boot-l
oader-tools-2.2.6.RELEASE.pom (0 B at 0 B/s)
Downloading from nexus-aliyun: http://maven.aliyun.com/nexus/content/groups/public/org/apache/commons/commons-compress/1.19/commons-compress-1.19.pom
Downloaded from nexus-aliyun: http://maven.aliyun.com/nexus/content/groups/public/org/apache/commons/commons-compress/1.19/commons-compress-1.19.pom (0 B at 0
B/s)
Downloading from nexus-aliyun: http://maven.aliyun.com/nexus/content/groups/public/org/apache/maven/maven-archiver/3.4.0/maven-archiver-3.4.0.pom
......
......

下面这个是启动相关的信息:

D:\myResource\maven\maven1>mvn spring-boot:run
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for com.theboyaply:maven1:jar:0.0.1-SNAPSHOT
[WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-compiler-plugin is missing. @ line 51, column 12
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]
[INFO]
[INFO] -----------------------< com.theboyaply:maven1 >------------------------
[INFO] Building maven1 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.2.6.RELEASE:run (default-cli) > test-compile @ maven1 >>>
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ maven1 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ maven1 ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ maven1 ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory D:\myResource\maven\maven1\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ maven1 ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.2.6.RELEASE:run (default-cli) < test-compile @ maven1 <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.2.6.RELEASE:run (default-cli) @ maven1 ---
[INFO] Attaching agents: []

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

2020-04-23 10:59:17.233  INFO 21004 --- [           main] com.theboyaply.maven1.Maven1Application  : Starting Maven1Application on DESKTOP-LR4IQHF with PID 210
04 (D:\myResource\maven\maven1\target\classes started by theAplyBoy in D:\myResource\maven\maven1)
2020-04-23 10:59:17.237  INFO 21004 --- [           main] com.theboyaply.maven1.Maven1Application  : No active profile set, falling back to default profiles: d
efault
2020-04-23 10:59:18.411  INFO 21004 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-04-23 10:59:18.420  INFO 21004 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-04-23 10:59:18.420  INFO 21004 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-04-23 10:59:18.509  INFO 21004 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2020-04-23 10:59:18.509  INFO 21004 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 12
12 ms
2020-04-23 10:59:18.659  INFO 21004 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-23 10:59:18.788  INFO 21004 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path '
'
2020-04-23 10:59:18.791  INFO 21004 --- [           main] com.theboyaply.maven1.Maven1Application  : Started Maven1Application in 1.992 seconds (JVM running fo
r 2.777)

可以从启动信息中发现,程序在 8080 端口上启动了(默认为 8080 端口),我们访问 /hello 接口:

http://localhost:8080/hello

效果如下:

maven-hello1

可以看见,使用 mvn 之后我们并不需要手动去寻找相关 jar 包添加进项目,而是使用 mvn 依赖的方式进行 jar 管理。mvn 项目管理依赖最重要的一个文件是 pom.xml,我们这个示例中的 pom.xml 文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.theboyaply</groupId>
    <artifactId>maven1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>maven1</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.2.6.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.2.6.RELEASE</version>
            </plugin>
        </plugins>
    </build>

</project>

上面的 pom.xml 是自动生成的,作用是将 springmvc 所有需要的包都自动导入了,并且还为我们提供了支持 mvn spring-boot:run 启动的功能。

通过 pom.xml 来进行配置,maven 会读取 pom.xml 中的配置信息,来加载我们项目中需要依赖的 jar 包,还有需要如何构件项目等等信息,都可以在 pom 文件中进行配置。

约定配置

maven 提倡使用一个共同的标准目录结构,maven 使用约定优于配置的原则,大家尽可能的遵守这样的目录结构,如下所示:

目录备注
$存放 pom.xml 和所有的子目录,即项目根目录
$/src/main/java项目的 java 源码
$/src/main/resources项目的资源,比如说 property 文件,spring.xml
$/src/test/java项目的测试类,比如说 junit 代码
$/src/test/resources测试用的资源
$/src/main/webapp/WEB-INFweb 应用文件目录,web 项目的信息,比如存放 web.xml、本地图片、jsp 视图页面
$/target打包输出目录
$/target/class编译输出目录
$/target/test-classes测试编译输出目录
Test.javamaven 只会自动运行符合该命名规则的测试类
~/.m2/repositorymaven 默认的本地仓库目录位置

结合表格中的约定信息,我们再去看看上面创建的 maven 项目,发现其目录结构与表格中约定的基本一致。

这是 maven 项目标准的结构,大家都按照这个约定来,然后 maven 中打包、运行、部署时候就非常方便了。

pom 文件

当我们在项目中需要用到maven帮我们解决jar包依赖问题,帮我们解决项目中的编译、测试、打包、部署时,项目中必须要有pom.xml文件,这些都是依靠pom的配置来完成的。

POM( Project Object Model,项目对象模型 ) 是 Maven 工程的基本工作单元,是一个XML文件,包含了项目的基本信息,用于描述项目如何构件,声明项目依赖,等等。

执行任务或目标时,Maven 会在当前目录中查找 POM。它读取 POM,获取所需的配置信息,然后执行目标。

POM 中可以指定以下配置:

  • 项目依赖
  • 插件
  • 执行目标
  • 项目构件 profile
  • 项目版本
  • 项目开发者列表
  • 相关邮件列表信息

在创建 POM 之前,我们首先需要描述项目组 (groupId),项目的唯一ID。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <!-- 模型版本 -->
    <modelVersion>4.0.0</modelVersion>
    
    <!-- 定义当前构件所属的组,通常与域名反向一一对应 -->
    <groupId>com.theboyaply</groupId>
    <!--项目的唯一ID,一个groupId下面可能多个项目,就是靠artifactId来区分的-->
    <artifactId>maven1</artifactId>
    <!-- 版本号 -->
    <version>0.0.1-SNAPSHOT</version>

</project>

maven 坐标

maven中引入了坐标的概念,每个构件都有唯一的坐标,我们使用maven创建一个项目需要标注其坐标信息,而项目中用到其他的一些构件,也需要知道这些构件的坐标信息。

maven中构件坐标是通过一些元素定义的,他们是groupId、artifactId、version、packaging、classifier,如我们刚刚上面创建的springboot项目,它的pom中坐标信息如下:

<groupId>com.theboyaply</groupId>
<artifactId>maven1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
  • goupId:定义当前构件所属的组,通常与域名反向一一对应。
  • artifactId:项目组中构件的编号。
  • version:当前构件的版本号,每个构件可能会发布多个版本,通过版本号来区分不同版本的构件。
  • package:定义该构件的打包方式,比如我们需要把项目打成jar包,采用java -jar去运行这个jar包,那这个值为jar;若当前是一个web项目,需要打成war包部署到tomcat中,那这个值就是war,可选(jar、war、ear、pom、maven-plugin),比较常用的是jar、war、pom,这些后面会详解。

上面这些元素中,groupId、artifactId、version是必须要定义的,packeage可以省略,默认为jar。

我们可以将上面创建的springboot项目发布出去,然后只需要告诉别人maven1这个项目的坐标信息,其他人就可以直接使用了,而且其他人用的时候,不用关心maven1运行需要依赖什么,maven1依赖的这些都会自动导入,非常方便。

dependencies依赖导入

maven可以帮我们引入需要依赖的构件(jar等),而maven是如何定位到某个构件的呢?

项目中如果需要使用第三方的jar,我们需要知道其坐标信息,然后将这些信息放入pom.xml文件中的dependencies元素中:

<project>
    <dependencies>
        <!-- 在这里添加你的依赖 -->
        <dependency>
            <groupId></groupId>
            <artifactId></artifactId>
            <version></version>
            <type></type>
            <scope></scope>
            <optional></optional>
            <exclusions>
                <exclusion></exclusion>
                <exclusion></exclusion>
            </exclusions>
        </dependency>
    </dependencies>
</project>
  • dependencies元素中可以包含多个dependency,每个dependency就表示当前项目需要依赖的一个构件的信息
  • dependency中groupId、artifactId、version是定位一个构件必须要提供的信息,所以这几个是必须的
  • type:依赖的类型,表示所要依赖的构件的类型,对应于被依赖的构件的packaging。大部分情况下,该元素不被声明,默认值为jar,表示被依赖的构件是一个jar包。
  • scope:依赖的范围,后面详解
  • option:标记依赖是否可选,后面详解
  • exclusions:用来排除传递性的依赖

通常情况下我们依赖的都是一些jar包,所以大多数情况下,只需要提供groupId、artifactId、version信息就可以了。

创建一个maven项目(maven2)

这次我们使用idea来创建。

打开idea,点击File->New->Project,选择Maven,如下:

idea-create-maven01

点击Next,输入项目的maven坐标信息,如下:

idea-create-maven02

点击Next,输入项目所在目录,如下:

idea-create-maven03

然后点击 Finish 完成项目的创建,项目结构如下:

idea-create-maven04

打开pom.xml,引入springmvc依赖,如下:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.2.1.RELEASE</version>
</dependency>

pom.xml文件最终如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.theboyaply</groupId>
    <artifactId>maven2</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.2.1.RELEASE</version>
        </dependency>
    </dependencies>

</project>

我们只引入了一个spring-web的jar包,springmvc还需要spring-core等jar包的支持,我们来看一下,maven是否帮我们也导入了。

pom.xml文件中,右键选中Show Dependencies,可以显示当前项目依赖的jar包,如下:

idea-create-maven05

idea-create-maven06

上图中显示了项目中的依赖信息,箭头有多级,从下至上,第一个箭头表示第一级,是直接依赖,后面的都是间接依赖,属于传递性依赖。

我们在maven2中添加了spring-web的依赖,并没有引入spring-beans、spring-core、spring-jcl的依赖,但是maven都自动帮我们导入了,这是因为spring-web的pom.xml中定义了它自己的依赖,当我们使用spring-web的时候,spring-web需要依赖的jar也会自动被依赖进来。

maven 依赖范围(scope)

我们的需求:

  1. 我们在开发项目的过程中,可能需要用junit来写一些测试用例,此时需要引入junit的jar包,但是当我们项目部署在线上运行了,测试代码不会再执行了,此时junit.jar是不需要了,所以junit.jar只是在编译测试代码,运行测试用例的时候用到,而上线之后用不到了,所以部署环境中是不需要的
  2. 我们开发了一个web项目,在项目中用到了servlet相关的jar包,但是部署的时候,我们将其部署在tomcat中,而tomcat中自带了servlet的jar包,那么我们的需求是开发、编译、单元测试的过程中需要这些jar,上线之后,servlet相关的jar由web容器提供,也就是说打包的时候,不需要将servlet相关的jar打入war包了
  3. 像jdbc的驱动,只有在运行的时候才需要,编译的时候是不需要的

这些需求怎么实现呢?

我们都知道,java中编译代码、运行代码都需要用到classpath变量,classpath用来列出当前项目需要依赖的jar包,这块不清楚的可以去看一下classpath和jar。

classpath和jar关系详解:https://www.theboyaply.cn/archives/classpath-and-jar

maven用到classpath的地方有:编译源码、编译测试代码、运行测试代码、运行项目,这几个步骤都需要用到classpath。

如上面的需求,编译、测试、运行需要的classpath对应的值可能是不一样的,这个maven中的scope为我们提供了支持,可以帮我们解决这方面的问题,scope是用来控制被依赖的构件与classpath的关系(编译、打包、运行所用到的classpath),scope有以下几种值:

compile

编译依赖范围,如果没有指定,默认使用该依赖范围,对于编译源码、编译测试代码、测试、运行4种classpath都有效,比如上面的spring-web。

test

测试依赖范围,使用此依赖范围的maven依赖,只对编译测试、运行测试的classpath有效,在编译主代码、运行项目时无法使用此类依赖。比如junit,它只有在编译测试代码及运行测试的时候才需要。

provided

已提供依赖范围。表示项目的运行环境中已经提供了所需要的构件,对于此依赖范围的maven依赖,对于编译源码、编译测试、运行测试中classpath有效,但在运行时无效。比如上面说到的servlet-api,这个在编译和测试的时候需要用到,但是在运行的时候,web容器已经提供了,就不需要maven帮忙引入了。

runtime

运行时依赖范围,使用此依赖范围的maven依赖,对于编译测试、运行测试和运行项目的classpath有效,但在编译主代码时无效,比如jdbc驱动实现,运行的时候才需要具体的jdbc驱动实现。

system

系统依赖范围,该依赖与3中classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显示第指定依赖文件的路径。这种依赖直接依赖于本地路径中的构件,可能每个开发者机器中构件的路径不一致,所以如果使用这种写法,你的机器中可能没有问题,别人的机器中就会有问题,所以建议谨慎使用。

如下:

<dependency>
    <groupId>com.theboyaply</groupId>
    <artifactId>rt</artifactId>
    <version>1.8</version>
    <scope>system</scope>
    <systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>

import

这个比较特殊,后面的文章中单独讲,springboot和springcloud中用到的比较多。

scope与classpath的关系

依赖范围编译源码编译测试代码运行测试运行项目示例
compileYYYYspring-web
test-YY-junit
providedYYY-servlet-api
runtime-YYYjdbc驱动
systemYYY-本地的jar包

scope如果对于运行范围有效,意思是指依赖的jar包会被打包到项目的运行包中,最后运行的时候会被添加到classpath中运行。如果scope对于运行项目无效,那么项目打包的时候,这些依赖不会被打包到运行包中。

依赖的传递

上面我们创建的maven-chat02中依赖了spring-web,而我们只引入了spring-web依赖,而spring-web又依赖了spring-beans、spring-core、spring-jcl,这3个依赖也被自动加进来了,这种叫做依赖的传递。

不过上面我们说的scope元素的值会对这种传递依赖会有影响。

假设A依赖于B,B依赖于C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,而A对于C是传递性依赖,而第一直接依赖的scope和第二直接依赖的scope决定了传递依赖的范围,即决定了A对于C的scope的值。

下面我们用表格来列一下这种依赖的效果,表格最左边一列表示第一直接依赖(即A->B的scope的值),而表格中的第一行表示第二直接依赖(即B->C的scope的值),行列交叉的值显示的是A对于C最后产生的依赖效果。

compiletestprovidedruntime
compilecompile--runtime
testtest--test
providedprovided-providedprovided
runtimeruntime--runtime

解释一下:

  1. 比如A->B的scope是compile,而B->C的scope是test,那么按照上面表格中,对应第2行第3列的值-,那么A对于C是没有依赖的,A对C的依赖没有从B->C传递过来,所以A中是无法使用C的
  2. 比如A->B的scope是compile,而B->C的scope是runtime,那么按照上面表格中,对应第2行第5列的值为runtime,那么A对于C是的依赖范围是runtime,表示A只有在运行的时候C才会被添加到A的classpath中,即对A进行运行打包的时候,C会被打包到A的包中
  3. 大家仔细看一下,上面的表格是有规律的,当B->C依赖是compile的时候(表中第2列),那么A->C的依赖范围和A->B的sope是一样的;当B->C的依赖是test时(表中第3列),那么B->C的依赖无法传递给A;当B->C的依赖是provided(表第4列),只传递A->C的scope为provided的情况,其他情况B->C的依赖无法传递给A;当B->C的依赖是runtime(表第5列),那么C按照B->C的scope传递给A
  4. 上面表格大家多看几遍,理解理解

maven依赖调解功能

现实中可能存在这样的情况,A->B->C->Y(1.0),A->D->Y(2.0),此时Y出现了2个版本,1.0和2.0,此时maven会选择Y的哪个版本?

解决这种问题,maven有2个原则:路径最近原则、最先声明原则

路径最近原则

上面A->B->C->Y(1.0),A->D->Y(2.0),Y的2.0版本距离A更近一些,所以maven会选择2.0。

但是如果出现了路径是一样的,如:A->B->Y(1.0),A->D->Y(2.0),此时maven又如何选择呢?

最先声明原则

如果出现了路径一样的,此时会看A的pom.xml中所依赖的B、D在dependencies中的位置,谁的声明在最前面,就以谁的为主,比如A->B在前面,那么最后Y会选择1.0版本。

可选依赖(optional元素)

有这么一种情况:

A->B中scope:compile
B->C中scope:compile

按照上面介绍的依赖传递性,C会传递给A,被A依赖。

假如B不想让C被A自动依赖,可以怎么做呢?

dependency元素下面有个optional,是一个boolean值,表示是一个可选依赖,B->C时将这个值置为true,那么C不会被A自动引入。

排除依赖

A项目的pom.xml中

<dependency>
    <groupId>com.theboyaply</groupId>
    <artifactId>B</artifactId>
    <version>1.0</version>
</dependency>

B项目1.0版本的pom.xml中

<dependency>
    <groupId>com.theboyaply</groupId>
    <artifactId>C</artifactId>
    <version>1.0</version>
</dependency>

上面A->B的1.0版本,B->C的1.0版本,而scope都是默认的compile,根据前面讲的依赖传递性,C会传递给A,会被A自动依赖,但是C此时有个更新的版本2.0,A想使用2.0的版本,此时A的pom.xml中可以这么写:

<dependency>
    <groupId>com.theboyaply</groupId>
    <artifactId>B</artifactId>
    <version>1.0</version>
    <exclusions>
        <exclusion>
            <groupId>com.theboyaply</groupId>
            <artifactId>C</artifactId>
        </exclusion>
    </exclusions>
</dependency>

上面使用使用exclusions元素排除了B->C依赖的传递,也就是B->C不会被传递到A中。

exclusions中可以有多个exclusion元素,可以排除一个或者多个依赖的传递,声明exclusion时只需要写上groupId、artifactId就可以了,version可以省略。

-- end --


评论