Maven实战-第六篇(Maven的依赖范围)

Maven实战-第六篇(Maven的依赖范围)

今天我们分享一下Maven中的依赖范围。因为在之前的Maven实战-第四篇(Maven的依赖配置)文章中已经对一部分内容进行了详细的介绍,所以本篇中涉及到上述的内容将不在过多的介绍了。

一、一般依赖范围

在Maven中依赖范围的目的控就是控制项目中classpath的关系。在Maven中主要有三种classpath。分别是编译的classpath、测试的classpath和运行的classpath。下面我们看一下不同依赖范围对上述三种classpath的影响。

1、compile

编译依赖范围,也是Maven默认的依赖范围。使用此依赖范围时表示,此组件对编译、测试和运行都有效。所以此依赖范围,对于上面的三个classpath都是有效的。


2、test

测试依赖范围。使用此依赖范围表示,此组件只对测试的classpath有效。对编译的和运行时无法使用此依赖。比较典型的组件为:Junit组件,具体配置如下:

 <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>

因为Maven实战-第四篇(Maven的依赖配置)文章已经详细介绍过了,所以这里就不在进行演示了。


3、provided

使用此依赖范围。组件将对编译和测试有效,而对运行时无效。比较典型的组件为:servlet-api。此组件我们编译和测试的时需要依赖,但在运行时,由于应用服务器已经提供了此组件的支持,所以不需要我们在项目中添加此依赖了。同样的因为之前文章中已介绍过了,所以这里就不在过多介绍了。


4、runtime

运行时依赖范围。也就是只对运行时才有效,但对测试也是有效的。典型的组件为数据库的驱动。只有项目运行时才需要。对其它的classpath是无效的。


5、system

系统依赖范围。system依赖范围和provided的依赖范围一致。唯一不同的之处就是在使用system依赖范围时,我们需要指定systemPath参数。因为system依赖范围并不是通过Maven仓库解析的,而是通过systemPath参数指定的路径解析的。具体的配置如下:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>system</scope>
    <systemPath>/Users/jilinwula/.m2/repository/junit/junit/4.13.2/junit-4.12.jar</systemPath>
</dependency>

比较典型的场景为加密项目中可能会使用。它会读取我们指定目录中的依赖包加载到项目中。例如从外部设备中读取。


6、import

导入依赖范围。此依赖范围和其它的依赖不同,它并不会影响编译、测试和运行的classpath。而是通过此依赖范围,将其它项目的依赖引入本项目中。所以在使用此依赖时,需要引入的依赖类型必须为pom类型。


下面是依赖范围和classpath之间的关系。

依赖范围(scope) 编译(classpath) 测试(classpath) 运行(classpath) 例子
compile Y Y Y spring-core
test - Y Y junit
provided Y Y - servlet-api
runtime - Y Y JDBC驱动
system Y Y - 本地的组件

横轴表示classpath,纵轴则表示Maven中的依赖范围。表示Y则表示对该classpath有效果,反之-则表示无效。

二、传递性依赖范围

上面介绍的是一般的依赖范围。但我们知道也就是上篇中介绍的,Maven中有传递性依赖,那么这个依赖会不会和一般的依赖范围有些不同呢。答案是肯定的。在Maven中依赖范围不仅可以控制依赖与classpath的关系,还对传递性依赖会产生影响。下面内容是依赖范围影响传递性依赖的关系:

compile test provided runtime
compile compile - - runtime
test test - - test
provided provided - provided provided
runtime runtime - - runtime

在Maven中如果项目A依赖于B,项目B又依赖于项目C,我们会说A对于B是第一直接依赖,B对于C是第二直接依赖,而A对于C则是传递性依赖。也就早篇中介绍的内容。所以在上面的表格中最左边的一列表示第一直接依赖范围,最上面一行表示的是第二直接依赖范围,中间的交叉单元格则表示传递性依赖的范围。


下面我们通过一个简单的例子来解释一下上面说的内容。假如我们有一个项目叫做account-email。该项目有一个com.icegreen:greenmail:1.3.1b的直接依赖,我们说这是第一直接依赖,并且假如该依赖范围是test。而greenmail又有一个javax.mail:mail:1.4的直接依赖,我们说这是第二直接依赖,其依赖范围是compile。显然javax.mail:mail:1.4是account-email的传递性依赖。按照上图所示,当第一直接依赖范围为test,第二直接依赖范围是compile的时候,传递性依赖的范围是test,因此javax.mail:mail:1.4是account-email的一个范围是test的传递性依赖。


我们通过下面的例子来验证一下上面说的内容。为了能够直观的观察第一依赖与第二依赖以及传递性依赖的关系。我们创建三个项目然后依次依赖,来验证上面说的内容。

1、mazhe-maven-user

  • pomx.ml
<?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">
    <parent>
        <artifactId>midai-mazhe-maven</artifactId>
        <groupId>cn.ma-zhe</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mazhe-maven-user</artifactId>
</project>
  • MazheMavenUser
public class MazheMavenUser {
    public static void main(String[] args) {
        System.out.println("Hello World Mazhe Maven User!");
    }
}

2、mazhe-maven-order

  • 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">
    <parent>
        <artifactId>midai-mazhe-maven</artifactId>
        <groupId>cn.ma-zhe</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mazhe-maven-order</artifactId>

    <dependencies>
        <dependency>
            <groupId>cn.ma-zhe</groupId>
            <artifactId>mazhe-maven-user</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
  • MazheMavenOrder
public class MazheMavenOrder {
    public static void main(String[] args) {
        System.out.println("Hello World Mazhe Maven Order!");
    }
}

3、mazhe-maven-mall

  • 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">
    <parent>
        <artifactId>midai-mazhe-maven</artifactId>
        <groupId>cn.ma-zhe</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mazhe-maven-mall</artifactId>

    <dependencies>
        <dependency>
            <groupId>cn.ma-zhe</groupId>
            <artifactId>mazhe-maven-order</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
  • MazheMavenMall
public class MazheMavenMall {
    public static void main(String[] args) {
        System.out.println("Hello World Mazhe Maven Mall!");
    }
}

我们创建了三个项目MazheMavenUser、mazhe-maven-order、mazhe-maven-mall。期中项目mazhe-maven-mall依赖于项目mazhe-maven-order,项目mazhe-maven-order又依赖于项目MazheMavenUser,所以项目MazheMavenUser就是项目mazhe-maven-mall的传递性依赖。下面我们按照上面的依赖关系来验证一下,当第一依赖是compile,第二依赖也是compile时,传递性依赖是不是也是compile的依赖范围。因为Maven中compile依赖范围为默认的依赖范围,所以我们不需要修改任何pom.xml直接在项目中验证一下。因为我们项目中引入了mazhe-maven-order依赖。所以因为传递性依赖的关系,Maven也会引入MazheMavenUser依赖。所以我们直接在MazheMavenMall中调用这两个项目的代码看看是否能成功的引用。

compile&compile

  • MazheMavenMall

public class MazheMavenMall {
    public static void main(String[] args) {
        System.out.println("Hello World Mazhe Maven Mall!");
        MazheMavenOrder.main(args);
        MazheMavenUser.main(args);
    }
}

*输出

Hello World Mazhe Maven Mall!
Hello World Mazhe Maven Order!
Hello World Mazhe Maven User!

我们看上面输出的日志,成功的验证了当第一依赖为compile,并且第二依赖也是compile的时候,传递性依赖的范围也是compile

compile
compile compile

Maven实战-第六篇(Maven的依赖范围)


compile&test

下面我们验证一下,当然第一依赖范围为compile,第二依赖范围为test的时候,传递性依赖的范围。因为是要修改的第二依赖范围,所以我们需要将mazhe-maven-order项目中的依赖范围修改为test

  • 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">
    <parent>
        <artifactId>midai-mazhe-maven</artifactId>
        <groupId>cn.ma-zhe</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mazhe-maven-order</artifactId>

    <dependencies>
        <dependency>
            <groupId>cn.ma-zhe</groupId>
            <artifactId>mazhe-maven-user</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

我们再次看一下mazhe-maven-mall项目中,是否有问题。

Maven实战-第六篇(Maven的依赖范围)

如上图所示,我们看到MazheMavenMall类中的调用MazheMavenUser代码报错,这是因为没有找到这个类的依赖所至。下面我们在mazhe-maven-mall项目中创建一个测试类,来验证一下测试代码是否能够成功的引用。

  • MazheMavenMallTest
public class MazheMavenMallTest {
    public static void main(String[] args) {
        System.out.println("Hello World Mazhe Maven Mall Test!");
        MazheMavenOrder.main(args);
        MazheMavenUser.main(args);
    }
}

Maven实战-第六篇(Maven的依赖范围)

我们看测试的代码也显示出无法引用MazheMavenUser类,这也间接证明了,当第一依赖范围为compile,第二依赖范围为test时候,Maven不会进行传递性依赖。

compile test
compile compile -

compile&provided

下面我们同样的验证一下,当然第一依赖范围为compile,第二依赖范围为provided的时候,传递性依赖的范围。我们同样的将mazhe-maven-order项目中的依赖范围修改为provided

  • 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">
    <parent>
        <artifactId>midai-mazhe-maven</artifactId>
        <groupId>cn.ma-zhe</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mazhe-maven-order</artifactId>

    <dependencies>
        <dependency>
            <groupId>cn.ma-zhe</groupId>
            <artifactId>mazhe-maven-user</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

我们同样的在看一下MazheMavenMall类的代码是否能成功的引入MazheMavenUser类。

Maven实战-第六篇(Maven的依赖范围)

我们在看一下MazheMavenMallTest类的代码是否能引入。

Maven实战-第六篇(Maven的依赖范围)

我们看上图所示,不管是主代码中还是测试代码中,都无法引入MazheMavenUser类,这也就说明了,当第一依赖范围为compile,第二依赖范围为provided的时候,Maven不会进行传递性依赖。

compile test provided
compile compile - -

compile&runtime

下面我们继续验证,当然第一依赖范围为compile,第二依赖范围为runtime的时候,传递性依赖的范围。我们继续的将mazhe-maven-order项目中的依赖范围修改为runtime

  • 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">
    <parent>
        <artifactId>midai-mazhe-maven</artifactId>
        <groupId>cn.ma-zhe</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mazhe-maven-order</artifactId>

    <dependencies>
        <dependency>
            <groupId>cn.ma-zhe</groupId>
            <artifactId>mazhe-maven-user</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
</project>

我们继续看MazheMavenMall类中的代码。

Maven实战-第六篇(Maven的依赖范围)

我们看上图所示,因为我们第二依赖范围是runtime,所以直接调用MazheMavenUser报错了。下面我们验证一下,如果项目运行时,会不会可以直接调用MazheMavenUser类。为了验证项目运行,我们需要通过反射的主式,来调用MazheMavenUser类,下面为具体的代码:

  • MazheMavenMall
import java.lang.reflect.Method;

public class MazheMavenMall {
    public static void main(String[] args) throws Exception {
        System.out.println("Hello World Mazhe Maven Mall!");
        MazheMavenOrder.main(args);
        Class<?> clazz = Class.forName("MazheMavenUser");
        Method method = clazz.getMethod("main", String[].class);
        method.invoke(null, (Object) args);
    }
}
  • 日志
Hello World Mazhe Maven Mall!
Hello World Mazhe Maven Order!
Hello World Mazhe Maven User!

我们看通过Java反射的方式,我们成功的调用了MazheMavenUser的方法,这也就说明了当第一依赖范围为compile,第二依赖范围为runtime的时候。Maven的传递性依赖范围为runtime。下面我们验证一下测试类的代码。

  • MazheMavenMallTest
public class MazheMavenMallTest {
    public static void main(String[] args) {
        System.out.println("Hello World Mazhe Maven Mall Test!");
        MazheMavenOrder.main(args);
        MazheMavenUser.main(args);
    }
}

Maven实战-第六篇(Maven的依赖范围)

我们看上图所示,测试类中的代码可以直接调用MazheMavenUser类的方法。这是因为runtime依赖范围是对测试和运行同样有效的,所以不会报错,我们也修改一下测试类的代码,来验证一下,测试类中运行是否有问题。

  • MazheMavenMallTest
import java.lang.reflect.Method;

public class MazheMavenMallTest {
    public static void main(String[] args) throws Exception {
        System.out.println("Hello World Mazhe Maven Mall Test!");
        MazheMavenOrder.main(args);
        MazheMavenUser.main(args);
        Class<?> clazz = Class.forName("MazheMavenUser");
        Method method = clazz.getMethod("main", String[].class);
        method.invoke(null, (Object) args);
    }
}
  • 日志
    Hello World Mazhe Maven Mall Test!
    Hello World Mazhe Maven Order!
    Hello World Mazhe Maven User!
    Hello World Mazhe Maven User!

这就证明了当第一依赖范围为compile,第二依赖范围为runtime的时候。Maven的传递性依赖范围为runtime。

compile test provided runtime
compile compile - - runtime

其它依赖范围的测试,由于和上面内容类似,我们就不依依验证了,我们只要参考上面的表格就可以知道传递性依赖的范围了。所以通过上面的表格我们可以发现这样的规律:

  • 当第二直接依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致。
  • 当第二直接依赖的范围是test的时候,Maven不会进行传递性依赖。
  • 当第二直接依赖的范围是provided的时候,只传递第一直接依赖范围也为provided的依赖,且传递性依赖的范围同样为provided。
  • 当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围为runtime。

给TA打赏
共{{data.count}}人
人已打赏
Maven

Maven实战-第五篇(Maven的传递性依赖)

2023-7-21 5:18:11

Maven

Maven实战-第七篇(Maven中的依赖冲突)

2023-7-24 7:16:05