Maven的依赖作用域和依赖传递

简介: Maven的依赖作用域和依赖传递

在Java项目开发中,Maven是我们最常用的依赖管理和构建工具了!我们常常通过添加dependency节点,就能够很方便地加入依赖,而不需要我们自己去手动下载jar文件并引入。

今天主要是来总结一下在Maven中依赖的作用域和传递。

1,依赖作用域

通过在每个dependency中设定scope字段,即可声明其作用域,例如:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.28</version>
    <!-- 声明作用域 -->
    <scope>provided</scope>
</dependency>

上面我们就设定了lombok这个依赖的作用域为provided

常用的作用域字段值及其意义如下:

  • compile 这是默认scope(即你不写scope字段的话,这个依赖作用域就是compile),表示依赖在编译测试运行时都是可用的,并且会参与项目的打包过程,该依赖会传递给依赖该模块的其他模块
  • provided 表示依赖在编译测试时是可用的,但该依赖不会参与程序运行阶段,即程序运行时无法调用该依赖中的类,它不会参与项目的打包过程,也不会传递给其他模块
  • runtime 表示依赖仅在运行时是可用的,但在编译和测试时不需要,它会传递给依赖该模块的其他模块,但不会参与项目的打包过程
  • test 表示依赖只在测试运行时使用,不会参与项目的打包过程,也不会传递给其他模块

可见一个Maven项目,从编译到运行会经历三个阶段:编译 → 测试 → 运行

不同作用域在三个阶段的可见性如下表:

编译时可用 测试时可用 运行时可用
compile
provided x
runtime x
test x x

2,作用域和打包

我们常常会使用maven-assembly-plugin插件,让我们在打包的时候将所有的依赖都打包至最后的jar文件中,使得jar文件可以直接运行。

不过真的是所有的依赖都会被打包到最后的jar中吗?

其实并不是,在使用maven-assembly-plugin插件插件时,默认只有scopecompileruntime的依赖才会被包含在最终的结果中

因此,为了减小最终jar的大小,我们应当将运行时不需要的依赖设置为provided或者test,当然这也是根据用途选择。

例如lombok依赖会在编译的时候生成gettersetter的代码,但是运行的时候这个依赖就不需要了,因此它常常被设定为provided

但是在Spring Boot开发中就不一样了,Spring Boot工程中,使用的是spring-boot-maven-plugin,这个插件也能完成同样的目的,即打包时将所有的依赖全部打包到一个jar中,但是这个插件会将所有的依赖都打包进去,无论其scope是什么。

不过,我们可以在这个插件中进行配置,声明打包时需要排除的依赖,例如:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <excludes>
            <!-- 打包时排除lombok依赖 -->
            <exclude>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </exclude>
        </excludes>
    </configuration>
</plugin>

3,依赖的传递

事实上,在Maven中,依赖也是会传递的,我们先创建一个名为first的项目,并引入lombok依赖如下:

<?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.gitee.swsk33</groupId>
    <artifactId>first</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>17</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- 引入lombok依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
        </dependency>
    </dependencies>
</project>

这是项目firstpom.xml文件,其groupIdcom.gitee.swsk33,其artifactIdfirst,其版本为1.0.0,现在在其工程目录下执行mvn clean install安装至本地仓库使得待会可以引用它。

现在在新建项目second,并引用上述的first作为依赖,如下:

<?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.gitee.swsk33</groupId>
    <artifactId>second</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>17</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- 引用first项目 -->
        <dependency>
            <groupId>com.gitee.swsk33</groupId>
            <artifactId>first</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>
</project>

好的,现在展开IDEA左侧栏的外部库部分,看看second项目的依赖:

image.png

可见second项目仅仅是引入了first项目,但为什么外部库中包含了lombok依赖呢?这是因为first依赖lombok,而second依赖first时,lombok也被传递给了second

image.png

同样地,如果现在又有一个项目third依赖second呢?那么third也会间接依赖lombok,也可以使用lombok中的类。

当然,依赖并不总是会传递的,有下列因素会影响依赖传递。

(1) scope作用域

scope不仅仅代表这个依赖的作用域,也会影响依赖的传递,只有scopecompileruntime的依赖是会传递的

假设现在把上述first项目中的lombok依赖scope改成provided或者test,然后重新执行mvn clean install,你就会发现在second的依赖中,就看不到lombok了!

(2) optional字段

除了scope之外,还可以设定依赖的optional字段,当设定为true时代表这个依赖是可选的,那么这时无论其scope是什么,这个依赖都不会传递。默认情况下,即不声明依赖的optional字段时,它的值是false

现在将first中的lombok依赖的optional字段声明为true

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.28</version>
    <optional>true</optional>
</dependency>

然后重新mvn clean install,再次打开second项目,你就会发现lombok依赖就没有传递过来了!

image.png

所以如果在制作外部库需要其他人引用的时候,我们可以将一些仅仅是外部库需要使用但是其它项目不一定要使用的依赖的optional设定为true,避免其他开发者引入你的外部库时发生依赖冲突。

4,optional带来的坑

对于optional虽然可以阻止依赖传递,但是使用时也需要注意,先来看一个例子。

现在仍然是创建一个名为first的项目,其groupIdcom.gitee.swsk33artifactIdfirst,版本为1.0.0,只引入hutool依赖并设定optionaltrue,整个项目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.gitee.swsk33</groupId>
    <artifactId>first</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>17</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- Hutool实用工具 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.16</version>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

然后创建一个类Test并调用hutool依赖中的类:

package com.gitee.swsk33.first;

import cn.hutool.core.io.FileUtil;

import java.io.File;

public class Test {
   

    public static void ls() {
   
        // 调用Hutool包中的FileUtil类
        File[] files = FileUtil.ls("E:\\中转");
        for (File file : files) {
   
            System.out.println(file.getName());
        }
    }

}

好的,现在在这个first项目目录下执行mvn clean install,以便后续别的项目引用。

现在再创建名为second的项目,依赖只引用上述first如下:

<!-- 引用first -->
<dependency>
    <groupId>com.gitee.swsk33</groupId>
    <artifactId>first</artifactId>
    <version>1.0.0</version>
</dependency>

并在主类中调用firstTest类中的方法如下:

import com.gitee.swsk33.first.Test;

public class Main {
   

    public static void main(String[] args) {
   
        // 调用first项目中的Test类的ls方法
        // ls方法调用了Hutool中的类
        Test.ls();
    }

}

运行,发现报错了:

image.png

很显然是找不到hutool依赖中的FileUtil类。

造成这个错误的原因是因为first的类Test中方法ls调用了外部依赖hutool的类,因此在其它项目(例如这个second项目)引用并调用first中的Test类的ls方法时,也是需要使用到依赖hutool中的类的

但是我们在first中将hutool设置为了optional,所以second中只引入first时,hutool并没有引入(没有传递到second项目依赖中):

image.png

这导致second项目中找不到hutool中的类。

所以解决这个问题的办法有下列几种:

  • 去掉firsthutool依赖的optional字段
  • second项目中引入hutool依赖

所以在我们自己开发外部库并给其他人使用时,就需要注意这个依赖可选性的问题。

相关文章
|
1月前
|
Java Maven
maven依赖原则以及jar包冲突
该文介绍了Maven依赖原则:最短路径优先,申明顺序优先和覆写优先。当有冲突时,Maven选择路径最短的版本,按POM中声明顺序加载,并且子POM的依赖优先于父POM。解决冲突最佳方式是通过`mvn dependency:tree`检查依赖树并调整POM文件中的坐标顺序。
28 2
|
2月前
|
IDE Java Maven
Spring Boot之如何解决Maven依赖冲突Maven Helper 安装使用
Spring Boot之如何解决Maven依赖冲突Maven Helper 安装使用
19 2
|
4月前
|
存储 Java 测试技术
Maven依赖范围
Maven依赖范围
97 0
|
5月前
|
Java Maven
maven 依赖剔除处理
maven 依赖剔除处理
24 0
|
5月前
|
Java 程序员 应用服务中间件
maven-依赖管理-下
maven-依赖管理-下
50 0
|
4天前
|
Java 项目管理 Maven
【揭秘】Maven聚合与继承:如何轻松实现项目依赖管理?
Maven的聚合和继承是Java开发中重要的概念。聚合允许将多个项目组合成一个构建单元,简化多模块项目的构建过程,提高构建效率。继承则让子项目重用父项目的配置和属性,避免了重复定义,增强了项目的一致性和可维护性。通过聚合和继承,Maven为多模块项目的构建和管理提供了高效且灵活的支持,减少了配置冗余,提升了开发效率。
【揭秘】Maven聚合与继承:如何轻松实现项目依赖管理?
|
8天前
|
Java Maven
Maven 引入外部依赖
在 Maven 项目中引入 LDAP 操作库 ldapjdk.jar,需将 jar 文件放入 src/lib 文件夹,并在 pom.xml 的 dependencies 部分添加依赖配置
|
11天前
|
Java Maven Spring
Maven 依赖搜索顺序
Maven在构建时按顺序搜索本地-&gt;中央仓库-&gt;已配置的远程仓库来查找依赖。若未设置远程仓库,会报错。可将默认仓库替换为阿里云仓库,通过修改settings.xml的mirrors节点或在repositories节点添加仓库地址。在pom.xml中指定依赖后,运行`mvn install`拉取。
|
26天前
|
Java Apache 项目管理
使用Maven进行Java项目构建与依赖管理
【4月更文挑战第16天】Apache Maven是Java项目的核心构建工具,它基于POM进行项目管理和构建自动化,简化构建过程并管理依赖。Maven提供标准化的目录结构、自动依赖解决、丰富的插件生态、多模块构建支持和版本管理功能。通过安装Maven、创建项目、配置依赖、构建及使用插件,开发者能高效管理Java项目,提升开发效率。了解和掌握Maven对于Java开发者至关重要。
|
29天前
|
Java 应用服务中间件 Maven
使用IDEA搭建SpringMVC环境,Maven导入了依赖,但是运行报错 java.lang.ClassNotFoundException
使用IDEA搭建SpringMVC环境,Maven导入了依赖,但是运行报错 java.lang.ClassNotFoundException
20 1

热门文章

最新文章

推荐镜像

更多