在 Maven 项目中,所有依赖通常都在 pom.xml 中通过 dependency 声明。但并不是所有依赖在项目生命周期的各个阶段都需要使用。例如,有些依赖只在 编译阶段 使用,有些只在 运行阶段 使用,还有一些只在 测试阶段使用。
为了解决这一问题,Maven 提供了 依赖作用域(Dependency Scope) 机制。
scope 用于定义依赖在 编译、运行、测试和打包阶段是否生效 ,从而决定该依赖是否会被加入到对应阶段的 类路径(classpath) 中。如果不显式指定 scope,默认值为:
一、Maven 的依赖管理机制
在 Maven 项目中,所有依赖都通过 pom.xml 文件声明,例如:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
如果不显式指定 scope,默认值为:
compile
当 Maven 构建项目时,会自动完成以下工作:
-
读取
pom.xml中定义的依赖 -
从远程仓库下载依赖
-
将依赖缓存到本地仓库
本地仓库位置:
~/.m2/repository
随后 Maven 会在 不同构建阶段 将这些依赖加入不同的类路径中。
二、什么是类路径(Classpath)
在 Java 中,类路径(Classpath) 是 JVM 或 Java 编译器用于查找 .class 文件和依赖库的位置集合。
简单来说:
类路径就是 Java 程序在运行或编译时,用来查找类文件的路径列表。
类路径通常包含以下几种资源:
-
项目编译后的
.class文件 -
第三方依赖库(jar)
-
项目资源文件(resources)
如果某个类不在类路径中,程序就会出现典型错误:
ClassNotFoundException
NoClassDefFoundError
而 Maven 的核心作用之一,就是 自动管理这些类路径依赖。
三、Maven 常见的五种 scope
Maven 常见的依赖作用域主要包括以下几种。
1 compile(默认作用域)
compile 是 Maven 的 默认作用域 ,如果不写 scope,依赖默认就是 compile。
特点:
-
编译阶段可用
-
运行阶段可用
-
测试阶段可用
-
打包时会被包含
例如:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
Spring 框架依赖通常就是 compile。
适用于:
-
项目核心依赖
-
应用运行必须依赖的库
2 provided
provided 表示 编译时需要,但运行时由外部环境提供。
特点:
-
编译阶段可用
-
运行阶段不可用
-
不会被打包
典型例子:
Servlet API
例如:
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
原因是:
Tomcat / Jetty 已经提供 Servlet API
如果再打包进去就会冲突。
3 runtime
runtime 表示 编译时不需要,但运行时需要。
特点:
-
编译阶段不可用
-
运行阶段可用
-
会被打包
典型例子:
JDBC驱动
例如:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
原因:
编译代码只依赖:
java.sql
但运行数据库时需要:
java.sql
4 test
test 作用域只在 测试阶段生效。
特点:
-
编译主代码不可用
-
运行主程序不可用
-
仅在测试代码中可用
例如:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
适用于:
JUnit
Mockito
TestNG
这些库只在测试时使用。
5 system
system 依赖是 Maven 早期提供的一种方式,用于 引用本地 jar 包。
例如:
<dependency>
<groupId>example</groupId>
<artifactId>example-lib</artifactId>
<scope>system</scope>
<systemPath>D:/libs/example.jar</systemPath>
</dependency>
缺点:
-
不参与 Maven 依赖管理
-
不可移植
因此 基本不推荐使用。
四、Maven scope 与类路径关系
Maven 在构建项目时,会根据 scope 决定依赖是否加入不同阶段的 classpath。
| scope | 编译 | 运行 | 测试 | 打包 |
|---|---|---|---|---|
| compile | ✔ | ✔ | ✔ | ✔ |
| provided | ✔ | ✘ | ✔ | ✘ |
| runtime | ✘ | ✔ | ✔ | ✔ |
| test | ✘ | ✘ | ✔ | ✘ |
因此可以这样理解:
Maven 会根据
pom.xml中依赖的scope,在 编译、运行和打包阶段构建不同的 classpath,从而决定哪些依赖在不同阶段被引入。