Maven---scope和依赖传递的介绍

做了开发几年了,项目中Maven没少用,却几乎没有深入了解过,一直把它作为管理各种依赖jar包的工具,平时偶尔有jar包冲突,也是百度解决。今天就好好理一理pom.xml里的一些标签如scope和option相关的作用。

本文的内容更多的是参考了《Maven实战》这本书,所以有些内容或例子会以此书为模板。

scope的作用

这个scope在书中的解释为依赖范围,大意是说在每段代码在编译,运行,以及测试时都会有一套自己的classpath,而每个依赖的scope就是指此依赖在每一个classpath下是否会起作用或者说是否可以被其它代码可见和调用。

在spring框架中,scope一般表示此bean的作用域。maven这里我更愿意称它为依赖(jar包)的作用域。比如说,如果某个jar包的scope为test类型,那么此依赖仅仅可以在Junit测试用例中可以编译运行。但在其它的classpath下不可见。

scope的种类

下面就详细说说每一种scope对应的classpath的作用情况吧。

compile

compile表示编译环境作用域。一般我们不指定scope默认就是此种作用域。此作用域为强依赖,也就是说只要pom文件中的某个依赖指定此作用域,那么代码中就一定可见且可以运行(特殊:jar包冲突会覆盖掉其中一个jar)。上面说过三种classpath,compile作用域的依赖在这三种classpath下都是可见的。

用spring-context依赖举个例子,看一看。

pom.xml


<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.0modelVersion>

    <groupId>com.binghuazheng.mavengroupId>
    <artifactId>mavendemoartifactId>
    <version>1.0.0-SNAPSHOTversion>

    <dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.1.2.RELEASEversion>
            <scope>compilescope>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <configuration>
                    <source>1.8source>
                    <target>1.8target>
                configuration>
            plugin>
        plugins>
    build>

project>

执行结果

Maven---scope和依赖传递的介绍_第1张图片
可以看到,依赖可以被使用,且能被测试用例正常调用。

test

test作用域,它的作用如同名字,依赖仅可以在Junit测试用例中编译执行,无法在正常的java目录中编译和运行。
将spring-context的作用域改为test类型。

<dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.1.2.RELEASEversion>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>
    dependencies>

下面的Demo.java是我们正常的src/main目录下的类,可以看到,此时编译都过不去,classpath下找不到spring-context相关的jar,但是Junit测试用例中是可以编译运行的。

Maven---scope和依赖传递的介绍_第2张图片

provided

表示已提供类型的jar包作用域,看起来有点迷惑。此作用域的jar包在src/main下的代码编译阶段以及测试用例阶段这两个classpath下是可见的,但在运行期不可见。通俗点说,就是此jar包在我们的代码中只可远观而不可亵玩,只能看不能摸。专业点说,就是代码只能编译不报错,但运行时找不到此jar。

好,将我们的spring-context的jar包作用域改为provided。

<dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.1.2.RELEASEversion>
            <scope>providedscope>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>
    dependencies>

然后在运行Demo.java看一看效果。

Maven---scope和依赖传递的介绍_第3张图片
这里相对于之前的test作用域来讲,首先编译是不报错了,但运行时报了ClassNotFound异常,也就是说运行期的classpath找不到此jar包。

runtime

运行期的作用域,此作用域的jar包,只能在代码运行时和测试用例中这两个classpath下可见。在编译期是找不到此jar包的。

<dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.1.2.RELEASEversion>
            <scope>runtimescope>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>
    dependencies>

再看一看Demo.java,已经变得编译不通过了。这里编译过不去,即使运行期jar包可见,暂时没办法运行了。

Maven---scope和依赖传递的介绍_第4张图片

system

系统级别的作用域,它需要和本地主机的环境变量绑定,此作用域用的很少。至少目前为止,我还没有在pom.xml内见过此级别的作用域,这里就不示例了。

import

import和上边的作用域其实没什么关系。它一般是和dependencyManagement关联使用的。当我们的工程依赖一个pom工程时,就需要指定scope为import。如果你自己搭建Spring Cloud的Demo工程,就会接触到Import。这里我们直接举个例子验证一下。

首先是我们的父工程,一般作为项目依赖管理,它的打包方式是pom,指定依赖版本–Finchley.SR2。


<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.0modelVersion>

    <groupId>com.binghuazheng.mavengroupId>
    <artifactId>mavendemoartifactId>
    <packaging>pompackaging>
    <version>1.0.0-SNAPSHOTversion>
    <modules>
        <module>child-demomodule>
    modules>


    <dependencyManagement>
        <dependencies>
            
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>Finchley.SR2version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <configuration>
                    <source>1.8source>
                    <target>1.8target>
                configuration>
            plugin>
        plugins>
    build>

project>

然后在看看它的子工程,子工程就可以依赖具体的jar了。


<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>mavendemoartifactId>
        <groupId>com.binghuazheng.mavengroupId>
        <version>1.0.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>child-demoartifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-netflix-eureka-serverartifactId>
        dependency>
    dependencies>


project>

Maven---scope和依赖传递的介绍_第5张图片
可以看到,这里它就将eureka-server相关的依赖引进来了,版本就是它的父工程的版本Finchley.SR2。

这里要注意,父工程的scope必须为import,且type必须为pom。否则子工程是引不到相关依赖的。

Maven---scope和依赖传递的介绍_第6张图片

总结

最后贴上《maven实战》一书中关于依赖范围和对应的classpath之间的关系,方便记忆。

Maven---scope和依赖传递的介绍_第7张图片

依赖范围Scope对依赖传递的影响

实际开发的项目中,各种依赖相互之间的关系很混乱。不同的依赖范围会导致不同的结果,看一下下面简单的例子。

Maven---scope和依赖传递的介绍_第8张图片
这里A工程依赖了B工程,依赖范围是test;而B工程内部又依赖了C工程,依赖范围是compile,那A工程也会将C工程导入进来,那C工程在A工程的classpath下的依赖范围Scope是什么呢?

先说下结果吧,C在A的classpath下是test作用域。

下面我们就研究一下关于不同依赖范围对依赖传递的影响。

在《Maven实战》这本书中,关于依赖传递有个专有名词—直接依赖。以上边demo为例,A直接依赖了B,A就是B的第一直接依赖;B依赖了C,B就是C的第二直接依赖;而C相对于A来说,它就是传递性的依赖。

上边几句话,用大白话解释。A通过B,进而拥有了C。A能不能使用B,取决于A中对B的scope。B能不能使用C,取决于B中对C的scope。那A能不能使用C,就取决于这其中两个scope的关系了。它们之间的判断关系如下图所示。

Maven---scope和依赖传递的介绍_第9张图片
用书中的解释对照上图关系,A对B的scope,就是第一列的关系;而B对C的scope,就是第一行的关系;最后A对C的scope就是内部具体对应的位置所示。横线表示依赖无法传递。

以上边A—B---C来说,A对C就是test传递范围。C只能在A的测试用例中编译运行,其它classpath下无法使用。

实际用代码验证看一看结果。

A工程依赖了B工程,这里B的scope为test。
pom.xml

    <dependencies>
        <dependency>
            <groupId>com.binghuazheng.mavengroupId>
            <artifactId>dependency-demoartifactId>
            <version>1.0.0-SNAPSHOTversion>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>
    dependencies>

B工程依赖了C工程,这里C的scope为compile。

    <dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.2.4.RELEASEversion>
            <scope>compilescope>
        dependency>
    dependencies>

回到A的代码里,首先看一看src/main/java里是否可用。

Maven---scope和依赖传递的介绍_第10张图片
编译错误,test作用域的jar,编译和运行classpath是看不见的,只能在测试classpath下可用,看一看Junit下是否可用。

Maven---scope和依赖传递的介绍_第11张图片
Junit环境下,正常运行,符合预期结果。

我们再测试一下,A中B的scope为provided,B中C的scope为runtime。那么A中C的Scope是否如传递依赖关系图中所标注的作用域provided?

分别修改对应的pom文件中的scope。然后看一看结果,记得要重新发布B工程的jar包。

Maven---scope和依赖传递的介绍_第12张图片
回到src/main/java中的Demo类中,发现它已经可以正常编译,但运行时就会报异常,找不到指定的class,符合provided的预期。

总结

好了,关于maven中依赖传递范围和传递性依赖的范围就到这里。平时的开发,可能遇到关于maven的bug不多,但一旦遇到了,如果没有相关的知识储备,基本就只能靠百度了,但这里的bug一般不会在百度找到一模一样的,或多或少都会关联一点具体的业务,所以还是有必要了解一下的。

你可能感兴趣的:(maven,java,maven)