一篇了解Maven中的<optional>和<scope>使用

发布时间:2023年12月26日

Maven的依赖传递

依赖管理是maven提供的主要功能之一,无论我们需要什么依赖,只需将它们添加到 POM.xml 中,在构建或运行时所有必要的类和资源都会自动添加到项目的 classpath 中。

Maven 中的依赖是有传递(Transitive)性的,默认会包含传递的依赖,这样就不用手动引用每一个依赖了。比如下面这个依赖关系中,A 依赖 B,B 依赖了 C……,如果你依赖 A 的话,就会自动包含 A/B/C/D/E

 A
  ├── B
  │   └── C
  │       └── D 
  └── E
      └── D 

但是传递依赖也带来了一个问题,比如下面这个例子:

  A
  ├── B
  │   └── C
  │       └── D 2.0
  └── E
      └── D 1.0

由于传递依赖,D 2.0 和 D 1.0 都会被加入 ClassPath 中,但因为它们版本不同,很可能会有包冲突等一系列问题。解决这个依赖传递导致的冲突问题,有两种方案

方案一: 使用者,也就是发起依赖方进行排除

<dependency>
  <groupId>group-a</groupId>
  <artifactId>artifact-a</artifactId>
  <version>1.0</version>
  <exclusions>
    <exclusion>
      <groupId>group-c</groupId>
      <artifactId>excluded-artifact</artifactId>
    </exclusion>
  </exclusions>
</dependency>

方案二: 提供方将依赖的范围定义为不传递,这样在构建时就不会包含这些不传递的依赖包了。不传递的配置有两种方式,也是本文讨论的重点

1): 定义 dependency scopeprovided

<dependency>
  <groupId>group</groupId>
  <artifactId>artifact-d</artifactId>
  <version>2.0</version>
    <!-- 不传递 -->
  <scope>provided</scope>
</dependency>

2): 定义 <optional>true

<dependency>
  <groupId>group</groupId>
  <artifactId>artifact-d</artifactId>
  <version>2.0</version>
  <!-- 不传递 -->
  <optional>true</optional>
</dependency>

在这两种不传递配置下,依赖关系都将在声明它们的模块的 classpath 中,但是使用将它们定义为依赖关系的模块不会在其他项目中传递它们,即不会形成依赖传递。

optional 与 scope 的区别

optional

可选的,可以理解为此依赖可选,如果不需要传递某项功能,可以不引用这个包。

scope provided

提供的,可以理解为此包不由我直接提供,需要调用者/容器提供

两者的使用场景介绍:

optional

现开发了一个类似Hibernate的框架,叫Summer吧,致敬下Spring,提供了多种数据库方言的支持:mysql/oracle/db2/postgresql…每种数据库支持也独立了一个module,Summer的依赖中配置了每种数据库的支持包:summer-mysql-support/summer-oracle-support…

但是实际引用此框架/依赖时,并不需要所有数据库方言的支持。此时可以把数据库的支持包都配置为可选的true。引用此框架时,只需按需引入自己需要的方言支持包即可,避免了冗余繁杂的依赖,也降低了jar包冲突的风险。

scope provided

现有一普通Web工程,必然会用到servlet-api这个包。但是实际上这个包一定是由容器提供的,因为我们这个web会部署到容器内,容器会提供servlet-api,如果此时项目中再引用的话就会造成重复引用,会有版本不一致的风险。

scope 的可选值

scope元素主要用来控制依赖的使用范围,指定当前包的依赖范围和依赖的传递性,也就是哪些依赖在哪些classpath中可用。常见的可选值有:compile, provided, runtime, test, system等。

compile(编译)

默认值。compile表示对应依赖会参与当前项目的编译测试运行等,是一个比较强的依赖。打包时通常会包含该依赖,部署时会打包到lib目录下。比如:spring-core这些核心的jar包。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

test(测试)

scope为test表示依赖项目仅参与测试环节,在编译、运行、打包时不会使用。最常见的使用就是单元测试类了。

类似单元测试这样的依赖,如果不设置scope为test,很显然它们会被打包、发布,但其实真是环境中并无什么作用。

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

runntime(运行时)

runntime仅仅适用于运行测试环节,在编译环境下不会被使用。比如编译时只需要JDBC API的jar,而只有运行时才需要JDBC驱动实现。

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

provided(已提供)

provide仅在编译测试阶段生效,provide不会被打包,也不具有传递性

比如:上面讲到的spring-boot-devtoolsservlet-api等,前者是因为不需要在生产中热部署,后者是因为容器已经提供,不需要重复引入。

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <scope>provided</scope>
</dependency>

system

system范围依赖与provided类似,不过依赖项不会从maven仓库获取,而需要从本地文件系统提供。使用时,一定要配合systemPath属性。不推荐使用,尽量从Maven库中引用依赖。

<dependency>
  <groupId>sun.jdk</groupId>
  <artifactId>tools</artifactId>
  <version>1.5.0</version>
  <scope>system</scope>
  <systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>
文章来源:https://blog.csdn.net/weixin_44147535/article/details/135232085
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。