Maven中optional标签详解

文章详细探讨了Maven中optional依赖的设置对项目构建和依赖传递的影响。通过代码演示,说明了optional为true时,依赖不会被传递,不会参与打包,且在调用时会导致找不到相关类。同时,文章讨论了父子工程间的依赖继承问题,即使在父工程设置optional为true,也不会影响子工程继承该依赖。最后,作者指出将optional设为true可以避免引入不必要的jar包。

这一篇文章应该是全网讲解optional最细致的,因为我都是拿我自己的试验来证明,从试验当中也发现了很多出乎意外的知识!感兴趣的跟着小编一块来学习呀!

一、前言

<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>5.8.18</version>
  <optional>true</optional>
</dependency>

optional表示是否会传递依赖,有两个可填值(假如不声明optional标签,默认就是false):

  • false: 传递依赖
  • true:不传递依赖

举例:A引用了B的依赖,而B又引用了C依赖。

  • 假如B引用C依赖的时候没有设置optional,那么A是可以使用C依赖的。
  • 假如B引用C依赖的时候将optional标签设置为了true,那么在A当中就无法使用C依赖相关的方法,并且A调用B依赖的方法,而B依赖方法使用到了C,这时候会报找不到C依赖下的类,因为C不参与A的打包。

二、依赖传递代码演示

(1)如下是一个父子聚合工程,不设置optional的情况:

打成jar包后,我们可以将jar解压,解压后在BOOT-INF的lib下存放着所能使用的依赖jar包,在这里可以看到hutool。

(2)设置optional为true的情况:

没有那也就意味着我再download工程不能访问hutool的类了,想要访问只能在当前项目再次引用了。

(3)我有点好奇,既然这里看不到那么他究竟会参与打包吗?假如不参与打包,我们download工程调用common中的方法,然后common中的方法又使用了hutool工具类,那能否访问成功?接下来我们来一点一点的印证!

设置为true的时候是否会参与download工程的打包?

答:他是不会参与download的打包的,打成jar包后,我们可以将jar解压,解压后在BOOT-INF的lib下存放着所能使用的依赖jar包。

假如不参与打包,我们download工程调用common中的方法,然后common中的方法又使用了hutool工具类,那能否访问成功?

答:不能访问成功,直接会报找不到hutool的类

我解压common包发现连BOOT-INF都没有,并不是只有common的jar包解压没有,而是所有的都没有。

我一直以为common包引用了hutool,那么解压common包就应该在BOOT-INF下的jar中看到hutool.jar,其实不是的,maven打包会将所有依赖关系全部放到当前项目的BOOT-INF/jar目录下

为此我专门在common当中写了一个方法,然后使用到了hutool当中的类。

然后我又在download工程下写了一个接口,让这个接口访问common包下test方法。

紧接着启动项目,启动download项目并没有异常,只要访问common包当中方法带有hutool相关的都报异常,说找不到hutool的包。因为我们刚刚也看过了,确实是没有这个jar包。

从而也证明了,不管是当前项目引用的,还是间接引用的,jar包都存放在BOOT-INF下/jar目录下,只要这下面没有jar,那当前项目就不能用。

三、为啥common的jar包下没有BOOT-INF呢?

在download下的BOOT-INF/lib下的common包解压后没有BOOT-INF我可以理解,因为所有的jar包都没有。但是为什么target当中的jar包解压后也没有,而download却有。他两个唯一的区别就是,一个是有main函数的web工程,一个是连main方法都没有的而且也没有打包插件。

我的父工程继承了spring-boot-starter-parent

而spring-boot-starter-parent当中对打包插件做了如下配置,也就意味着子工程一旦声明该插件就会遵循如下配置:

于是我在common包下添加了打包插件。注意使用打包插件意味着打出来的jar是一个可以直接执行的jar,因此必须要声明main函数,否则会打包异常的。

并且新增了一个主函数

于是再次打包,这时候会发现jar包大小已经发生了变化。解压后会发现他已经存在BOOT-INF了。

但是诡异的一幕发生了,download引入了common包,并且download调用了common包下的方法,直接就mvn install 不通过了。说在仓库找不到包。

但是在仓库当中实际上是存在这个包的。那也就是证明项目不能引用可执行jar。

于是我又把common的打包插件给去掉之后mvn install可以成功了。因此也证明了假如我们要封装自己的jar,一定不要使用打包插件。一旦使用插件就会变成了可执行jar,可执行jar是不可以被别的项目所引用的。

四、是否会影响父子工程之间的依赖继承呢?

假如我在聚合工程的父pom依赖当中使用optional为true,那子工程会继承吗?接下来进行演示。

注:在父工程设置optional为true,并不会影响子工程继承该依赖。

五、总结

  1. 将依赖设置为true不仅代表着依赖不会传递,就连打包的时候都不会将该jar打包进去,一旦使用到调用该jar包的方法就会异常。
  2. 在父工程设置optional为true,并不会影响子工程继承该依赖。
  3. 不管是当前项目引用的,还是间接引用的,所有依赖jar包都存放在jar解压后的BOOT-INF下/jar目录下,只要这下面没有jar,那当前项目就不能用。

什么时候将optional设置为true?

就拿hutool工具类来说,如下图:你想用他的某些工具类,他还让你引用一些第三方的依赖,为什么他不直接引用到自己的项目?

实际上hutool他肯定是引用了的,如果不引用他的项目可能连编译都编译不过,更别提打包给我们用了,他是将这个依赖设置为了true,假如谁用到了这块的功能,谁自己引入这个依赖。这样可以规避掉一些没有用到这块功能但是却引入了没有用的jar包。

<think>首先,用户的问题是关于Maven中的scope标签的用法和含义。我需要基于系统级指令来构建我的回答。系统级指令要求:-所有行内数学表达式必须使用$...$格式。-独立公式必须使用$$...$$格式并单独成段。-LaTeX语法正确。-使用中文回答。-生成相关问题。-在回答中引用的段落末尾自然地添加引用标识,例如[^1]。-回答结构清晰,帮助用户逐步解决问题,尽量保证回答真实可靠。用户提供的引用是:-引用[1]:关于Maven常用标签,包括parent,dependencies,dependencyManagement,packaging,scope。-引用[2]:关于<optional>和<scope>在Starter开发中的用法。-引用[3]:详细解释scope的取值和作用。-引用[4]:关于<scope>的作用,特别是import作用域。用户的问题:我想了解Maven中scope标签的用法和含义请问Mavenscope标签的作用及常见用法所以,我需要专注于scope标签。回答应该结构清晰,逐步解释。大纲我的回答:1.**介绍scope标签的作用**:解释scope在Maven中的基本作用,即指定依赖的作用范围和传递性。2.**常见scope取值**:列出常见的scope值,如compile,provided,runtime,test,system,import,并解释每个的含义和作用范围。3.**详细解释每个scope**:基于引用[3]和[4],提供更详细的信息。4.**使用场景和示例**:给出实际例子,如使用provided用于ServletAPI,test用于JUnit等。5.**最佳实践**:讨论如何避免依赖冲突、优化打包体积等,基于引用[4]。6.**相关问题**:在最后生成相关问题。确保引用标识:在引用内容末尾添加[^n]。例如,如果我从引用[3]取信息,就在那段末尾加[^3]。使用中文回答。现在,结构化回答:-**标题或开头**:直接回答问题。-**主体**:分点解释。-**结尾**:生成相关问题。数学表达式:用户的问题是关于Maven的,可能不需要数学表达式。但指令说必须使用$...$或$$...$$。问题中没有数学内容,所以我可以避免使用,但如果有机会,比如在解释依赖传递时,可以用简单的数学符号。但最好保持相关。或许在解释范围时,可以用集合表示,但可能过度了。指令说"所有行内数学表达式",如果不需要,就不要强加。看引用,没有数学内容。所以,我可以不包含数学表达式,除非必要。但指令是强制的,所以如果有变量或公式,就用。例如,在解释依赖关系时,说“依赖传递性可以用$D\rightarrowT$表示”,但可能不自然。为了安全,我可以在解释时简单用一下,比如“作用范围如$compile$表示编译时依赖”。但最好只在必要时使用。既然用户问题不涉及数学,我可以专注于文本。生成相关问题:在回答末尾,添加“相关问题”部分。引用标识:当引用具体信息时添加。例如,从引用[3]取scope取值表时加[^3]。现在,构建回答内容:1.**作用介绍**:scope标签用于指定依赖的作用范围和传递性,帮助控制依赖在不同阶段(编译、测试、运行时)的可用性[^3][^4]。2.**常见取值**:-compile:默认值,适用于所有阶段,包括编译、测试、运行时。依赖会传递[^3]。-provided:类似于compile,但依赖由JDK或容器提供,不会打包到最终制品中[^3][^4]。-runtime:在运行时需要,但编译时不需要。依赖会传递[^3]。-test:仅在测试阶段使用,不会传递[^3]。-system:类似于provided,但依赖来自文件系统,需要指定路径。不推荐使用[^3]。-import:用于在dependencyManagement中导入依赖,实现多继承[^4]。3.**详细解释**:基于引用[3],提供表格或列表。4.**示例**:如<scope>provided</scope>用于servlet-api,因为容器会提供[^3]。5.**最佳实践**:使用provided排除容器依赖;使用test减少打包;import用于灵活依赖管理[^4]。使用清晰的语言。最后,生成相关问题:1.Mavenscope标签如何影响依赖传递?2.在实际项目中,如何选择合适的scope值?3.Maven的import作用域有什么具体应用场景?等等。确保回答真实可靠:基于提供的引用。</think>### Maven scope标签的作用及常见用法 在Maven项目中,`<scope>`标签用于指定依赖项的作用范围,控制依赖在不同构建阶段(如编译、测试、运行时)的可用性和传递性。这有助于避免依赖冲突、优化打包体积,并提升项目的可维护性。下面我将逐步解释其作用和常见用法。 #### 1. **scope标签的基本作用** - **作用范围控制**:`<scope>`定义了依赖项在哪些阶段有效,例如编译阶段需要、测试阶段需要或运行时需要。这确保了依赖只在必要场景被引入,避免冗余[^3]。 - **依赖传递性**:部分scope值会影响依赖是否传递给其他模块。例如,`compile`范围的依赖会传递到依赖当前模块的项目中,而`test`范围的依赖则不会传递[^3][^4]。 - **打包影响**:scope决定了依赖是否包含在最终构建的制品(如JAR或WAR文件)中。例如,`provided`范围的依赖不会打包,因为它们由运行环境(如Tomcat容器)提供[^3][^4]。 这些机制帮助开发者管理复杂依赖关系,减少“依赖地狱”(dependency hell)问题[^4]。 #### 2. **常见的scope取值及含义** 以下是Maven中最常用的scope取值,每个值对应不同的作用范围和传递性。基于引用[^3]的详细解释,我整理如下表: | scope取值 | 有效范围 | 依赖是否传递 | 是否打入最终制品 | 典型示例 | |-----------|-----------------------------|--------------|------------------|------------------------------| | `compile` | 编译、测试、运行时 | 是 | 是 | `spring-core`(核心库依赖) | | `provided`| 编译、测试阶段 | 否 | 否 | `servlet-api`(由Web容器提供) | | `runtime` | 测试、运行时 | 是 | 是 | JDBC驱动(如`mysql-connector-java`) | | `test` | 仅测试阶段 | 否 | 否 | `JUnit`(单元测试框架) | | `system` | 编译、测试阶段 | 是 | 否 | 本地系统路径的JAR文件(不推荐) | | `import` | 仅用于`dependencyManagement` | 是 | 否 | 导入其他POM的依赖管理配置 | **详细说明**: - **`compile`(默认值)**:适用于所有阶段。依赖会传递到子模块,并打包到制品中。适用于核心库,如`spring-core`[^3]。 - **`provided`**:依赖在编译和测试时可用,但运行时由环境提供(如Servlet API在Tomcat中)。不会打包,避免与容器冲突[^3][^4]。 - **`runtime`**:依赖在运行时和测试时需要,但编译时不需要。例如数据库驱动,在编译时可能无需直接引用[^3]。 - **`test`**:仅在测试代码中使用,如单元测试库(JUnit)。不传递也不打包,减少制品大小[^3]。 - **`system`**:类似`provided`,但依赖需通过`<systemPath>`指定本地文件路径。不推荐使用,因为会破坏构建的可移植性[^3]。 - **`import`**:特殊作用域,仅用于`<dependencyManagement>`部分。它允许导入另一个POM的依赖管理配置,实现多继承(例如在Spring Boot Starter中)。依赖本身不会直接引入项目,而是继承其版本管理[^4]。示例用法: ```xml <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` #### 3. **实际用法示例** - **避免依赖冲突**:在Web项目中,使用`provided`范围排除Servlet API,因为容器会提供它: ```xml <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> ``` 这确保构建的WAR文件不包含重复依赖[^3][^4]。 - **优化测试依赖**:为测试库设置`test`范围,仅在测试阶段生效: ```xml <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> ``` 这样JUnit不会污染生产代码[^3]。 - **多模块项目管理**:使用`import`范围在父POM中导入共享配置,简化依赖版本控制: ```xml <dependencyManagement> <dependencies> <dependency> <groupId>com.example</groupId> <artifactId>shared-dependencies</artifactId> <version>1.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` 这比传统`<parent>`继承更灵活[^4]。 #### 4. **最佳实践建议** - **减少打包体积**:优先使用`provided`或`test`排除不必要的依赖(如容器提供的库或测试工具),这能显著缩小制品大小[^4]。 - **处理传递性问题**:如果需阻断依赖传递,结合`<optional>true</optional>`标签(标记依赖为可选)或调整scope(如设为`test`)。例如,在开发Spring Boot Starter时,使用`optional`避免强制传递依赖[^2][^4]。 - **避免`system`范围**:因其依赖本地路径,会导致构建不可移植。改用Maven仓库管理依赖[^3]。 - **性能优化**:合理选择scope能加速构建过程,例如`test`范围依赖只在测试阶段解析[^4]。 通过正确使用`<scope>`标签,您可以提升项目的健壮性和可维护性。如果您有具体项目场景,我可以提供更针对性的建议。 ### 相关问题 1. Maven的`<scope>`标签如何影响依赖传递性? 2. 在实际项目中,如何选择合适的scope值来避免依赖冲突? 3. Maven的`import`作用域在Spring Boot Starter开发中有哪些具体应用? 4. `<scope>`标签与`<optional>`标签的区别和协作方式是什么? [^1]: 引用[1]: Maven常用标签及作用,包括parent标签、dependencies和dependencyManagement的区别等。 [^2]: 引用[2]: 一文解析Maven的<optional>与<scope>在Starter开发中的用法与区别。 [^3]: 引用[3]: 【Maven】属性scope依赖作用范围详解,包含scope取值、有效范围和示例。 [^4]: 引用[4]: Maven依赖中<scope>的作用,强调import作用域的应用和使用建议。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怪 咖@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值