彻底搞懂Maven的依赖范围(scope)

maven提供了以下几种依赖范围:
  • compile

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

如:

   <dependencies>
        <dependency>
            <groupId>com.chengroupId>
            <artifactId>HelloWorldartifactId>
            <version>1.0-SNAPSHOTversion>
            <scope>compilescope>
        dependency>
    dependencies>
  • test

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

如:

   <dependencies>
        <dependency>
            <groupId>com.chengroupId>
            <artifactId>HelloWorldartifactId>
            <version>1.0-SNAPSHOTversion>
            <scope>testscope>
        dependency>
    dependencies>
  • provide

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

如:

   <dependencies>
        <dependency>
            <groupId>com.chengroupId>
            <artifactId>HelloWorldartifactId>
            <version>1.0-SNAPSHOTversion>
            <scope>providescope>
        dependency>
    dependencies>
  • runtime

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

如:

   <dependencies>
        <dependency>
            <groupId>com.chengroupId>
            <artifactId>HelloWorldartifactId>
            <version>1.0-SNAPSHOTversion>
            <scope>runtimescope>
        dependency>
    dependencies>
  • system

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

如:

   <dependencies>
        <dependency>
            <groupId>com.chengroupId>
            <artifactId>HelloWorldartifactId>
            <version>1.0-SNAPSHOTversion>
            <scope>systemscope>
            <systemPath>${basedir}/lib/threadpool4j-1.0.0.jarsystemPath>
        dependency>
    dependencies>

要搞懂上面的意思,我们先来认识一下java的classpath

Java中,我们经常听到classpath这个东西,classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索class。
因为Java是编译型语言,源码文件是.java,而编译后的.class文件才是真正可以被JVM执行的字节码。因此,JVM需要知道,如果要加载一个com.chen.Hello的类,应该去哪搜索对应的Hello.class文件。简单地说就是去classpath指定路径下去找对应的.class文件
所以,classpath就是一组目录的集合,比如

C:\Users\Administrator\Desktop\tt;C:\java;"D:\My project\project1\bin"

现在假设classpath是 .;C:\Users\Administrator\Desktop\tt;C:\java .表示当前目录,当JVM在加载com.chen.Hello这个类时,会依次查找:

  • <当前目录>\com\chen\Hello.class
  • C:\Users\Administrator\Desktop\tt\com\chen\Hello.class
  • C:\java\com\chen\Hello.class

如果JVM在某个路径下找到了对应的class文件,就不再往后继续搜索。如果所有路径下都没有找到,就报错。

classpath的设定方法有两种:

  • 在系统环境变量中设置classpath环境变量,不推荐;
  • 在启动JVM时设置classpath变量,推荐。

没有设置系统环境变量,也没有传入-cp参数,那么JVM默认的classpath为.,即当前目录。

知道classpath后,举个例子,我们创建一个目录,创建一个java文件,并在类中输出 Hello World!:

public class Test {
    public static void main(String[] args) {
        System.out.println("Hello,World!");
    }
}

在该目录下执行编译javac 得到 Test.class 文件
彻底搞懂Maven的依赖范围(scope)_第1张图片
如果我们不指定classpath,并且在 Test.class 所在的目录执行 cmd,是可以正确输出的:
在这里插入图片描述
但是我们不指定classpath,并且不是在 Test.class 所在的目录执行 cmd,是不可以正确输出的:
在别的目录执行:
在这里插入图片描述
但是我们指定classpath,并且不是在 Test.class 所在的目录执行 cmd,是可以正确输出的:
指定classpath且在别的目录执行:
在这里插入图片描述
可以看到输出了Hello,World! -cp是-classpath 的简写,两个可以互换, -cp指定jvm到只在这个路径下去找Test,即路径 C:\Users\Administrator\Desktop\tt\Test.class

jar包

我们熟悉的jar包,其实就是一个目录,就是一个zip格式的压缩文件。向上面的如果有很多很多.class文件,散落在各层目录中,肯定不便于管理和使用。这时候jar包就可以发挥作用了,把目录打一个包,变成一个文件,它可以把package组织的目录层级,以及各个目录下的所有文件(包括.class文件和其他文件)都打成一个jar文件,这样一来,就简单多了。

我们创建一个项目:
彻底搞懂Maven的依赖范围(scope)_第2张图片

package com.chen;

public class Test {
    public static void main(String[] args) {
        System.out.println("Hello,World!");
    }
}

创建好后,打包成jar包,把它赋值到别的地方,我们在这个地方进行测试
彻底搞懂Maven的依赖范围(scope)_第3张图片
执行

java -cp ./demo2-1.0-SNAPSHOT.jar com.chen.Test

在这里插入图片描述
看到输出了 Hello,World!

因为jar包就是zip包,所以,找到正确的目录,点击右键,在弹出的快捷菜单中选择“添加到压缩文件”,“压缩(zipped)文件夹”,就制作了一个zip文件。然后,把后缀从.zip改为.jar,一个jar包就创建成功,还有就是可以借助maven来获取jar包。

通过上面我们知道,java中编译代码、运行代码都需要用到classpath变量,classpath用来列出当前项目需要依赖的jar包

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

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

  • compile
  • test
  • provide
  • runtime
  • system

我们打包一个jar包,并在我们项目中引用,通过改变scope来看看
引用我们本地打包的HelloWorld

    <dependencies>
        <dependency>
            <groupId>com.chengroupId>
            <artifactId>HelloWorldartifactId>
            <version>1.0-SNAPSHOTversion>
        dependency>
    dependencies>

1,compile
把scope 改成 compile ,这是默认的

    <dependencies>
        <dependency>
            <groupId>com.chengroupId>
            <artifactId>HelloWorldartifactId>
            <version>1.0-SNAPSHOTversion>
            <scope>compilescope>
        dependency>
    dependencies>

源码
彻底搞懂Maven的依赖范围(scope)_第4张图片

编译测试代码
彻底搞懂Maven的依赖范围(scope)_第5张图片
可以看到在编译源码、编译测试代码都可以找到HelloWorld类

2,test
把scope 改成 test

 <dependencies>
        <dependency>
            <groupId>com.chengroupId>
            <artifactId>HelloWorldartifactId>
            <version>1.0-SNAPSHOTversion>
            <scope>testscope>
        dependency>
    dependencies>

源码
彻底搞懂Maven的依赖范围(scope)_第6张图片

编译测试代码
彻底搞懂Maven的依赖范围(scope)_第7张图片
可以看到在只在编译测试代码可以找到HelloWorld类

3,provide
把scope 改成 provide

    <dependencies>
        <dependency>
            <groupId>com.chengroupId>
            <artifactId>HelloWorldartifactId>
            <version>1.0-SNAPSHOTversion>
            <scope>providescope>
        dependency>
    dependencies>

彻底搞懂Maven的依赖范围(scope)_第8张图片
可以看到在编译源码、编译测试代码都可以找到HelloWorld类

但是我们执行package命令就会报错:
彻底搞懂Maven的依赖范围(scope)_第9张图片

4,runtime
把scope 改成 runtime

    <dependencies>
        <dependency>
            <groupId>com.chengroupId>
            <artifactId>HelloWorldartifactId>
            <version>1.0-SNAPSHOTversion>
            <scope>runtimescope>
        dependency>
    dependencies>

彻底搞懂Maven的依赖范围(scope)_第10张图片

可以看到在只在编译测试代码可以找到HelloWorld类

其实要理解这几种scope 值,并不难,只要了解了java的classpath就可以了

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