maven之scope
概览
在 Maven 中,scope
定义了依赖的可见性和生存周期,主要影响依赖在编译、测试和运行阶段的可用性,以及是否传递给下游模块。以下是 Maven 中常用的 scope
类型及其作用:
1. compile(默认范围)
- 作用:
- 依赖在编译、测试和运行时都可用。
- 是大多数依赖的默认范围,例如一些基础库(如日志框架、通用工具库等)。
- 特点:
- 传递性:会传递到下游模块。
- 典型场景:
- 引入需要在代码中直接使用的类库(如 Apache Commons、Guava 等)。
2. provided
- 作用:
- 依赖在编译和测试阶段可用,但在运行时需要由运行环境提供。
- 特点:
- 不传递到下游模块。
- 典型场景:
- Servlet API、JSP API 等,通常由应用服务器(如 Tomcat、Jetty)提供。
- 示例:
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope>
</dependency>
3. runtime
- 作用:
- 依赖在测试和运行阶段可用,但编译阶段不可用。
- 特点:
- 传递性:会传递到下游模块。
- 典型场景:
- 依赖只在运行时需要,例如 JDBC 驱动。
- 示例:
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version><scope>runtime</scope>
</dependency>
4. test
- 作用:
- 依赖只在测试阶段可用(编译和运行测试时)。
- 特点:
- 不传递到下游模块。
- 典型场景:
- 测试框架(如 JUnit、Mockito)。
- 示例:
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.9.3</version><scope>test</scope>
</dependency>
5. system
- 作用:
- 类似于
provided
,但需要显式提供依赖的 JAR 路径。
- 类似于
- 特点:
- 不推荐使用,容易导致依赖路径硬编码。
- 必须通过
systemPath
指定本地文件路径。 - 不传递到下游模块。
- 示例:
<dependency><groupId>com.example</groupId><artifactId>example-lib</artifactId><version>1.0</version><scope>system</scope><systemPath>${project.basedir}/libs/example-lib.jar</systemPath>
</dependency>
6. import
- 作用:
- 专用于
<dependencyManagement>
中,用于导入一个依赖的 BOM(Bill of Materials)。
- 专用于
- 特点:
- 通常用于聚合模块或管理版本。
- 示例:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>3.1.4</version><type>pom</type><scope>import</scope>
</dependency>
7. 总结对比
Scope | 编译 | 测试 | 运行 | 传递性 | 典型用途 |
---|---|---|---|---|---|
compile | ✔️ | ✔️ | ✔️ | ✔️ | 通用依赖(默认) |
provided | ✔️ | ✔️ | ❌ | 由容器提供的依赖 | |
runtime | ✔️ | ✔️ | ✔️ | JDBC 驱动等运行时依赖 | |
test | ✔️ | ❌ | 单元测试框架 | ||
system | ✔️ | ✔️ | ✔️ | ❌ | 手动指定本地依赖(不推荐) |
import | N/A | BOM 导入 |
通过合理设置 scope
,可以优化依赖的管理和传递,避免冗余依赖影响构建效率和运行环境。
Q&A
如果我有一个common包被多个项目依赖,common中增加了一个依赖,但是想让引入common包的依赖方选择是否引入该新增的依赖,应该怎么选择scope?
如果你希望 common
包中新增的依赖由依赖方自行决定是否引入,通常的做法是将该依赖的 scope
设置为 **provided**
。以下是具体原因和实现方式:
原因
**provided**
** 的特性**:- 在
common
包中,这个依赖仅用于编译和测试阶段,但不会随着common
包传递给下游模块。 - 下游项目需要该功能时,可以显式添加这个依赖;不需要时,可以忽略。
- 在
- 保持依赖的灵活性:
- 使用
provided
,你可以避免强制下游模块引入不需要的依赖,减少潜在的依赖冲突。
- 使用
实现方式
在 common
的 pom.xml
中,将压缩依赖的 scope
设置为 provided
:
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-compress</artifactId><version>1.23.0</version><scope>provided</scope>
</dependency>
下游项目的选择
- 需要该依赖的项目: 在项目的
pom.xml
中显式引入压缩依赖:
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-compress</artifactId><version>1.23.0</version>
</dependency>
- 不需要该依赖的项目: 不添加该依赖即可,
provided
范围不会影响它们。
注意事项
**optional**
** 的对比**:- 另一种方案是将压缩依赖标记为
**optional**
,这会阻止该依赖传递到下游模块,但依赖方仍可选择显式添加:
- 另一种方案是将压缩依赖标记为
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-compress</artifactId><version>1.23.0</version><optional>true</optional>
</dependency>
- 区别:* `optional` 适用于完全可选的功能。* `provided` 更适合运行环境提供依赖的场景,或你希望控制依赖的范围。
- 文档说明: 在
common
包的文档或 README 中,清楚说明压缩功能的依赖需求,避免依赖方困惑。 - 避免类路径问题: 如果
common
包中使用了压缩库的代码,但下游项目未引入该依赖,可能会出现ClassNotFoundException
或NoClassDefFoundError
。务必提醒依赖方注意这一点。