Maven中scope=provided和optional=true的区别
先说效果,maven依赖声明中加了<scope>provided</scope>
,或者加了<optional>true</optional>
,从效果上看是一样的,都会中断依赖传递,观察下图:
![](https://img2024.cnblogs.com/blog/572188/202406/572188-20240604140913951-1248170654.webp)
图中,项目B分别依赖了C和D,只不过一个声明了optional=true,一个声明了scope=provided,然后项目A再声明了B的依赖,此时在项目A环境中,既没有C,也没有D,所以在效果上看,它们是一样的。
那它们有哪些区别呢?
先看一下maven官方文档上对二者的描述:
- optional:
If project Y depends on project Z, the owner of project Y can mark project Z as an optional dependency, using the "optional" element. When project X depends on project Y, X will depend only on Y and not on Y's optional dependency Z. The owner of project X may then explicitly add a dependency on Z, at her option. (It may be helpful to think of optional dependencies as "excluded by default.")
- provided
This is much like compile, but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scope provided because the web container provides those classes. A dependency with this scope is added to the classpath used for compilation and test, but not the runtime classpath. It is not transitive.
大概意思是说,在A项目依赖B项目提供了一些特性,但又不想让这些特性默认提供,而是作为可以选择的附加功能,默认不提供,需要声明后(主动添加B项目的依赖)才生效,这时用optional;而对于provided,文档侧重提到了运行环境
概念,强调只在编译时存在,而运行时不存在的依赖,也就是说,provided的主要用途不是为了考虑依赖是否传递,而是要看项目运行时是不是不应该有这个依赖(是不是需要jvm或者运行容器提供)。
经常拿scope=provided来举例的经典场景之一,就是servlet-api
这个依赖了,在代码coding阶段需要使用到它的一些api,而在实际运行时,它的作用要由具体的运行容器来实现,因此编译时可以有它,而打成war包放到tomcat环境下运行时,war包里面不应该有这个servlet-api.jar
,否则就会报错了。
在实际的spring-boot项目中,由于大部分使用了内置的undertow
或者tomcat
容器,已经不需要特别声明这个jar的provided属性了。事实上,日常中更经常需要被用到的,应该主要就是这个optinal了,比如你要提供一组基础jar包,供项目组中的其他同事在他们的项目中引入依赖使用时,如果你提供的某些依赖了其他jar包来做的功能并不一定会被使用到,便可以用到这个optinal了。特别是用到诸如@ConditionalOnClass
这种检测项目中是否存在某个class的判断条件时,更是用optional的好时机。
而provided
的使用场景,除了servlet-api
,lombok
也很适合:A项目使用lombok做了一些代码生成,完成开发需要deploy到私服仓库之前,记得要将lombok的依赖加上<scope>provided</scope>
,因为它的作用周期已经在A项目打包完成时结束了,对于依赖A项目的其他项目,不需要用到lombok这个玩意儿,它们需要的是A项目提供的功能,而不是附带的帮助自己生成代码的额外功能;也不应该用optional
,因为没什么好选择的,它并不是A项目提供的可选功能之一。
maven scope 'provided' 和 ‘compile’的区别
解释
其实这个问题很简单。
对于scope=compile的情况(默认scope),也就是说这个项目在编译,测试,运行阶段都需要这个artifact(模块)对应的jar包在classpath中。
而对于scope=provided的情况,则可以认为这个provided是目标容器已经provide这个artifact。换句话说,它只影响到编译,测试阶段。在编译测试阶段,我们需要这个artifact对应的jar包在classpath中,而在运行阶段,假定目标的容器(比如我们这里的liferay容器)已经提供了这个jar包,所以无需我们这个artifact对应的jar包了。
实例(scope=provided)
比如说,假定我们自己的项目ProjectABC 中有一个类叫C1,而这个C1中会import这个portal-impl的artifact中的类B1,那么在编译阶段,我们肯定需要这个B1,否则C1通不过编译,因为我们的scope设置为provided了,所以编译阶段起作用,所以C1正确的通过了编译。测试阶段类似,故忽略。
那么最后我们要吧ProjectABC部署到Liferay服务器上了,这时候,我们到$liferay-tomcat-home\webapps\ROOT\WEB-INF\lib
下发现,里面已经有了一个portal-impl.jar了,换句话说,容器已经提供了这个artifact对应的jar,所以,我们在运行阶段,这个C1类直接可以用容器提供的portal-impl.jar中的B1类,而不会出任何问题。
实际插件的行为
刚才我们讲述的是理论部分,现在我们看下,实际插件在运行时候,是如何来区别对待scope=compile和scope=provided的情况的。
做一个实验就可以很容易发现,当我们用maven install生成最终的构件包ProjectABC.war后,在其下的WEB-INF/lib中,会包含我们被标注为scope=compile的构件的jar包,而不会包含我们被标注为scope=provided的构件的jar包。这也避免了此类构件当部署到目标容器后产生包依赖冲突。
依赖范围
maven中三种classpath
编译,测试,运行
1.compile:默认范围,编译测试运行都有效
2.provided:在编译和测试时有效
3.runtime:在测试和运行时有效
4.test:只在测试时有效
5.system:在编译和测试时有效,与本机系统关联,可移植性差
pom.xml常用元素介绍
project 包含pom一些约束的信息
modelVersion 指定当前pom的版本
groupId(主项目标示,定义当前maven属于哪个项目,反写公司网址+项目名)、
artifactId(实际项目模块标识,项目名+模块名)、
version(当前项目版本号,第一个0标识大版本号,第二个0标示分支版本号,第三个0标识小版本号,0.0.1,snapshot快照,alpha内部测试,beta公测,release稳定,GA正式发布)
name项目描述名
url项目地址
description项目描述
developers开发人员列表
licenses许可证
organization:组织
dependencies:依赖列表
dependency:依赖项目 里面放置坐标
scope:包的依赖范围 test
optional :设置依赖是否可选
exclusions:排除依赖传递列表
dependencyManagement 依赖的管理
build:为构建行为提供支持
plugins:插件列表
parent:子模块对父模块的继承
modules:聚合多个maven项目
Maven scope: provided、 compile、import_scope为compile-CSDN博客
scope=compile(默认)
对于scope=compile的情况(默认scope),也就是说这个项目在编译,测试,运行阶段都需要这个jar包在classpath中。
scope=provided
scope=provided的情况,则可以认为这个provided是目标容器已经provide这个jar。换句话说,它只影响到编译,测试阶段。而在运行阶段,假定目标的容器(比如我们这里的tomcat容器)已经提供了这个jar包,app可以直接使用容器提供的jar,所以无需我们打包对应的jar包了。
scope=import
解决maven继承(单)问题
Maven的继承和Java的继承一样,是无法实现多重继承的,一个子模块只能有一个<parent>标签。如果这个父模块有十几个子模块,那这个父模块的dependencyManagement会包含大量的依赖,不利于管理。
scope依赖能解决这个问题。在dependencyManagement中通过scope依赖,就可以引入多个type=pom的dependency。例如可以写这样一个用于依赖管理的pom:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<!-- <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> lookup parent from repository
</parent> -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.chen.export</groupId>
<artifactId>export-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.chen.log</groupId>
<artifactId>log-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
scope的其他参数如下:
runtime
表示dependency不作用在编译时,但会作用在运行和测试时,如JDBC驱动,适用运行和测试阶段。
test
表示dependency作用在测试时,不作用在运行时。 只在测试时使用,用于编译和运行测试代码。不会随项目发布。
system
跟provided 相似,但是在系统中要以外部JAR包的形式提供,maven不会在repository查找它