Maven optional scope Maven中optional和scope元素的使用弄明白了吗
secbro2 人气:0在梳理项目的过程中发现很多开发同学对 Maven 依赖文件的配置并不了解,特别是对 Maven 的 optional 元素和 scope 元素的使用也非常随意。这就会导致发布的 jar 包或 war 包非常 “胖”、编译速度慢,而且还很容易生产 jar 冲突等问题。本篇文章从 optional 和 scope 的使用场景入手,让项目实现一波瘦身。
optional 元素
这里以 Spring Boot 项目中的使用为例,比如我们在项目中经常使用的热部署组件 spring-boot-devtools,就可以使用 optional 元素来进行定义,对应 pom 文件中配置如下:
<!--devtools 热部署--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
那么,这里的 optional 元素设置为 true 表示何意?optional 是 Maven 依赖 jar 时的一个选项,表示该依赖是可选的,项目之间依赖不传递。不设置 optional(默认)或者 optional 是 false,表示传递依赖。
文字描述可能比较抽象,下面用具体实例场景来进行更直观的描述,这里假设有两个项目 A 和 B,其中 A 为父项目,B 为子项目。在父项目中引入了单元测试的依赖:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
下面针对 optional 元素的不同值进行讲解。
optional 元素默认值(false)
当父项目添加 junit 依赖时,并未添加 optional 选项,也就是默认的 optional 元素的值为 false。
此时,当子项目依赖父项目时,父项目 A 和子项目 B 的关系如下:
父项目并未设置 optional 元素为 true,那么便具有依赖传递性。此时,子项目 B 中会直接引入父项目 A 中引入的 Junit 的 jar 包。也就是说 B 项目打包时,jar/war 包中会包含 junit 的 jar 包。
optional 元素为 true
当父项目引入 junit 依赖时,设置 optional 元素为 true。那么,子项目 B 便有了更多的选择。
如果项目 B 不需要 Junit 的 jar 包,那么在其 pom 文件中不需进行任何处理便可以。如果 B 项目也需要对应的 jar 包依赖,可以有两种选择:第一、A 项目中对应依赖的 optional 设置为 false 或去掉;第二、B 项目中直接引入需要的该依赖。
parent 继承的情况
我们经常会在 parent 项目中配置统一的依赖版本控制,如下:
<dependencyManagement> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <optional>true</optional> </dependency> </dependencies> </dependencyManagement>
此时,如果 B 项目需要 Junit 的 jar 包,可以直接在项目中引入,这里父项目中的 optional 配置对子项目并无影响。
综上所述,在 Maven 项目中,恰当的使用 optional 配置,可以在很大程度上减少 jar 包的大小,提升编译和发布速度。
scope 元素
上面讲完了 optional 元素的使用,再来看看 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 表示依赖项目仅参与测试环节,在编译、运行、打包时不会使用。最常见的使用就是单元测试类了:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
类似单元测试这样的依赖,如果不设置 scope 为 test,很显然它们会被打包、发布,但其实真是环境中并无什么作用。
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(已提供)
provided 适合在编译和测试的环境,和 compile 功能相似,但 provide 仅在编译和测试阶段生效,provide 不会被打包,也不具有传递性。
比如:上面讲到的 spring-boot-devtools、servlet-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>
scope 依赖的传递性
上面讲解 scope 的不同参数值,针对这些参数值,在多个项目中的依赖传递性如下:
其中 B 依赖 A,C 依赖 B,传递性的关键是 B 依赖 A 时所设置的 scope 值,当 B 采用不同的值时对应的依赖关系如下:当 B 通过 test 或 provided 依赖 A 时,C 不依赖 A;当 B 通过 runtime 或 compile 依赖 A 时,C 依赖 A;
小结
经过上面对 Maven 的 optional 和 scope 元素的详细讲解,想必你已经可以对项目中的配置进行排查,确保精确的使用项目与项目之间的依赖,而不是每个项目都引入大而全的依赖文件,导致项目臃肿,编译缓慢,冲突不断。
加载全部内容