浅谈Maven依赖中的Scope

514 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第10天,点击查看活动详情

1. 背景

当我们配置Java项目的依赖时,我们会发现有一些依赖还会配置<scope>,大部分不用配置,这是为什么呢?本文就给大家简要介绍一下maven依赖中的scope

2.不是没配,默认compile

当我们没有配置时,maven默认写上了compile,下面我们看一下它的官方解释:

This is the default scope, used if none is specified. Compile dependencies are available in all classpaths of a project. Furthermore, those dependencies are propagated to dependent projects.

简言之,此默认项会使依赖伴随这个项目的一生,从编译到打包最后的成品,它一直都在。那如果我们不需要它一直都在呢?我们想只是编译的时候,最后生成jar包了,就没用了。比如lombok,那我们怎么办呢?

3. 其它常见的scope

3.1. lombok 选择 provided

provided 的官方解释是:编译测试有,运行时没了。这里大家可以自己做实验,看看写成这样,最后打完的包里的lib目录下还有没有lombok?😀还要注意一点,它不传递依赖,所以,最好每个模块声明一下自己要不要。

This is much like compile, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scope provided because the web container provides those classes. A dependency with this scope is added to the classpath used for compilation and test, but not the runtime classpath. It is not transitive.

就像下面这样:

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
    <scope>provided</scope>
</dependency>

3.2. junit选择test

test,顾名思义,用作测试时的依赖库,它也不传递依赖。

This scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases. This scope is not transitive. Typically this scope is used for test libraries such as JUnit and Mockito. It is also used for non-test libraries such as Apache Commons IO if those libraries are used in unit tests (src/test/java) but not in the model code (src/main/java).

就像下面这样

<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

3.3.mysql-connector-java可以选择runtime

runtime也会偶然见到,在运行时使用。这位同学就在工作中遇见了:maven scope runtime

This scope indicates that the dependency is not required for compilation, but is for execution. Maven includes a dependency with this scope in the runtime and test classpaths, but not the compile classpath.

就会像下面这样:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.26</version>
    <scope>runtime</scope>
</dependency>

但是,如果你在maven仓库中查找,你会发现它给出的示例中,没有用runtime,所以,你的观点是什么?评论区见吧。

3.4.不被推荐的system

通常,没有见过大场面的人应该对它不熟悉,因为它通常用作打包时依赖一个本地路径才有的jar包。system 元素与 provided 元素类似,但是被依赖项不会从 maven 仓库中查找,而是从本地系统中获取,systemPath 元素用于制定本地系统中 jar 文件的路径。一般像下面这样:

<dependency>
    <groupId>com.mytest</groupId>
    <artifactId>test</artifactId>
    <version>1.0</version>
    <scope>system</scope>
    <systemPath>${pom.basedir}/lib/test-1.0.jar</systemPath>
</dependency>

但是我真的不推荐您这么做,我更建议您将这个包发布到maven私服,或者安装到本地仓库,然后使用。不然,你也可能出现这位同学出现的问题:通过<scope>system</scope>依赖本地jar包时,要注意了!

4. import了解一下!

通常,我们会在<dependencyManagement>中看到它,知道它会协助你添加一系列的依赖的版本,但是,相当一部分人不知道它的具体作用。

首先,你需要弄清楚导入依赖,简言之,它是为了方便引入依赖的,可以把一些依赖的版本号先封装起来,然后以pom的方式引入即可。

这里,我们需要复习依赖传递,什么是依赖传递呢?

依赖传递 (Transitive Dependencies)是Maven 2.0开始的提供的特性,依赖传递的好处是不言而喻的,可以让我们不需要去寻找和发现所必须依赖的库,而是将会自动将需要依赖的库帮我们加进来。例如A依赖了B,B依赖了C和D,那么你就可以在A中,像主动依赖了C和D一样使用它们。

但依赖传递也不可避免的会带来一些问题,例如:当依赖层级很深的时候,可能造成循环依赖 (cyclic dependency) 当依赖的数量很多的时候,依赖树会非常大.

其实,用过maven几年的小伙伴应该知道,往往打包失败、线上运行的时候异常,有一定概率需要查一下依赖是不是有冲突,从庞大的依赖树里慢慢翻找。为了避免传递依赖,我们可以使用import。更深入的,大家参考这篇文章:maven pom类型,有import和没有import的区别

后记

希望大家通过今天的文章,对于mavenscope有一个浅显的理解,在未来的开发过程中,可以再结合实践,去深入探究。