Maven和Gradle包管理
1. Maven
1.1. Maven是什么
Maven 是 Apache 软件基金会组织维护的一款专门为 Java 项目提供构建和依赖管理支持的工具。
一个 Maven 工程有约定的目录结构,约定的目录结构对于 Maven 实现自动化构建而言是必不可少的一环,就拿自动编译来说,Maven 必须 能找到 Java 源文件,下一步才能编译,而编译之后也必须有一个准确的位置保持编译得到的字节码文件。
我们在开发中如果需要让第三方工具或框架知道我们自己创建的资源在哪,那么基本上就是两种方式:
- 通过配置的形式明确告诉它
- 基于第三方工具或框架的约定 Maven 对工程目录结构的要求
1.2. Maven依赖
Maven 中最关键的部分,我们使用 Maven 最主要的就是使用它的依赖管理功能。当 A jar 包用到了 B jar 包中的某些类时,A 就对 B 产生了依赖,那么我们就可以说 A 依赖 B。
依赖管理中要解决的具体问题:
- jar 包的下载:使用 Maven 之后,jar 包会从规范的远程仓库下载到本地
- jar 包之间的依赖:通过依赖的传递性自动完成
- jar 包之间的冲突:通过对依赖的配置进行调整,让某些 jar 包不会被导入
1.3. 依赖范围
maven的依赖范围包括: compile,provide,runtime,test,system。
- compile:表示编译范围,指 A 在编译时依赖 B,该范围为默认依赖范围。编译范围的依赖会用在编译,测试,运行,由于运行时需要,所以编译范围的依赖会被打包。
- provided:provied 依赖只有当 jdk 或者一个容器已提供该依赖之后才使用。provide 依赖在编译和测试时需要,在运行时不需要。例如:servlet api被Tomcat容器提供了。
- runtime:runtime 依赖在运行和测试系统时需要,但在编译时不需要。例如:jdbc 的驱动包。由于运行时需要,所以 runtime 范围的依赖会被打包。
- test:test 范围依赖在编译和运行时都不需要,只在测试编译和测试运行时需要。例如:Junit。由于运行时不需要,所以 test 范围依赖不会被打包。
- system:system 范围依赖与 provide 类似,但是必须显示的提供一个对于本地系统中 jar 文件的路径。一般不推荐使用。
编译时不需要。例如:jdbc 的驱动包。由
于运行时需要,所以 runtime 范围的依赖会被打包。
test:test 范围依赖在编译和运行时都不需要,只在测试编译和测试运行时需要。例如:Junit。由
于运行时不需要,所以 test 范围依赖不会被打包。
system:system 范围依赖与 provide 类似,但是必须显示的提供一个对于本地系统中 jar 文件的
路径。一般不推荐使用。
1.4. 依赖的传递
- A 依赖 B,B 依赖 C,那么在 A 没有配置对 C 的依赖的情况下,A 里面能不能直接使用 C?
Maven的依赖传递是指,当项目A依赖项目B,而项目B又依赖项目C时,Maven会自动将项目C的依赖传递给项目A,不需要在项目A的pom.xml中显式声明对C的依赖。 - 再以上的前提下,C 是否能够传递到 A,取决于 B 依赖 C 时使用的依赖范围。
B 依赖 C 时使用 compile 范围:可以传递
B 依赖 C 时使用 test 或 provided 范围:不能传递,所以需要这样的 jar 包时,就必须在需要的地方明确配置依赖才可以。
1.5. 依赖的排除
当 A 依赖 B,B 依赖 C 而且 C 可以传递到 A 的时候,A 不想要 C,需要在 A 里面把 C 排除掉。而往往这种情况都是为了避免 jar 包之间的冲突。
所以配置依赖的排除其实就是阻止某些 jar 包的传递。因为这样的 jar 包传递过来会和其他 jar 包冲突。
一般通过使用excludes标签配置依赖的排除:
2. Gradle
2.1. Gradle是什么
Gradle是继Maven之后的新一代构建工具,它采用基于groovy的DSL语言作为脚本,相比传统构建工具,通过XML来配置而言,最直观上的感受就是脚本更加的简洁、优雅。如果你之前对Maven有所了解,那
么可以很轻易的转换到Gradle,它采用了同Maven一致的目录结构,可以与Maven一样使用Maven中央仓库以及各类仓库的资源,并且Gradle默认也内置了脚本转换命令可以方便的将POM转换为gradle.build。
2.1.1. 标准结构
Gradle遵循COC(convention over configuration约定优于配置)的理念,默认情况下提供了与maven相同的项目结构配置
大体结构如下:
project root
src/main/java(测试)
src/main/resources
src/test/java(测试源码目录)
src/test/resources(测试资源目录)
src/main/webapp(web工程)
在一些老项目上,可能目录结构并不是标准结构,然而一般开发人员又不好进行结构调整.此时可以通过配置sourceSet来指定目录结构
sourceSets {
main {
java {
srcDir 'src/java'
}
resources {
srcDir 'src/resources'
}
}
}
2.2. Gradle脚本
一个简单的Gralde脚本,或许包含如下内容,其中标明可选的都是可以删掉的部分
- 插件引入:声明你所需的插件
- 属性定义(可选):定义扩展属性
- 局部变量(可选):定义局部变量
- 属性修改(可选):指定project自带属性
- 仓库定义:指明要从哪个仓库下载jar包
- 依赖声明:声明项目中需要哪些依赖
- 自定义任务(可选):自定义一些任务
//定义扩展属性(给脚本用的脚本)
buildScript {
repositories {
mavenCentral()
}
}
//应用插件,这里引入了Gradle的Java插件,此插件提供了Java构建和测试所需的一切。
apply plugin: 'java'
//定义扩展属性(可选)
ext {
foo="foo"
}
//定义局部变量(可选)
def bar="bar"
//修改项目属性(可选)
group 'pkaq'
version '1.0-SNAPSHOT'
//定义仓库,当然gradle也可以使用各maven库 ivy库 私服 本地文件等,后续章节会详细介绍(可选)
repositories {
mavenLocal()
maven {
url 'https://maven.aliyun.com/repository/public/'
}
mavenCentral()
}
//定义依赖,这里采用了g:a:v简写方式,加号代表了最新版本(可选)
dependencies {
compile "cn.pkaq:ptj.tiger:+"
}
//自定义任务(可选)
task printFoobar {
println "${foo}__${bar}"
}
2.3. Gradle依赖管理
通常而言,依赖管理包括两部分,对依赖的管理以及发布物的管理;依赖是指构建项目所需的构件(jar包等)。例如,对于一个应用了spring普通的java web项目而言,spring相关jar包即项目所需的依赖。发布物,则是指项目产出的需要上传的项目产物。
2.3.1. 采用变量统一控制版本号
dependencies {
def bootVersion = "1.3.5.RELEASE"
compile "org.springframework.boot:spring-boot-starter-
web:${bootVersion}",
"org.springframework.boot:spring-boot-starter-data-
jpa:${bootVersion}",
"org.springframework.boot:spring-boot-starter-
tomcat:${bootVersion}"
}
2.3.2. 自动获取最新版本依赖
如果你想某个库每次构建时都检查是否有新版本,那么可以采用+来让Gradle在每次构建时都检查并应用最新版本的依赖。当然也可以采用1.x,2.x的方式来获取某个大版本下的最新版本。
dependencies {
compile "org.springframework.boot:spring-boot-starter-web:+"
}
2.4. Gradle坐标
仓库中构件(jar包)的坐标是由configurationName "group:name:version:classifier@extension"组成的字符串构成,如同Maven中的GAV坐标,Gradle可借由此来定位你想搜寻的jar包。
在gradle中可以通过以下方式来声明依赖:
testCompile group: 'junit', name: 'junit', version: '4.0'
2.5. 外部依赖
可以通过如下方式声明外部依赖,Gradle支持通过map方式或者gv的简写方式传入依赖描述,这些声明依赖会去配置的repository查找。
dependencies {
// 采用map方式传入单个
compile group: 'commons-lang', name: 'commons-lang', version: '2.6'
// 采用map方式传入多个
compile(
[group: 'org.springframework', name: 'spring-core', version: '2.5'],
[group: 'org.springframework', name: 'spring-aop', version: '2.5']
)
// 采用简写方式声明
compile 'org.projectlombok:lombok:1.16.10'
// 采用简写方式传入多个
compile 'org.springframework:spring-core:2.5',
'org.springframework:spring-aop:2.5'
}
2.6. 项目依赖
此类依赖多见于多模块项目,书写方式如下,其中:是基于跟项目的相对路径描述compile project(':project-foo')
3.问题聚合
3.1Maven的优点和缺点?
Maven是一个流行的Java项目管理和构建工具,它具有许多优点和缺点。以下是一些可能的优点和缺点:
优点:
- 标准化管理:Maven提供了一种标准化的项目管理和构建方式,使得项目更加易于管理和维护。它提供了一组共享的插件和约定,使项目构建过程更加一致和可靠。
- 依赖管理:Maven能够管理项目的依赖关系,包括自动下载和管理库和框架的依赖。这使得项目更加易于集成和测试,减少了手动配置和管理的需要。
- 自动化构建:Maven能够自动化构建项目,使得代码编译、测试、打包和部署等过程更加快速和可靠。这有助于减少错误和提高代码质量。
- 集成测试:Maven能够集成测试框架,如JUnit、TestNG等,使得测试更加容易和可靠。它还可以生成测试报告,以便于跟踪和解决测试中的问题。
- 文档生成:Maven能够生成项目的文档,包括JavaDoc、Scaladoc等,这有助于项目成员更好地理解和使用代码。
缺点: - 学习曲线陡峭:Maven使用了一种基于XML的配置文件来定义项目的构建过程,对于初学者来说可能比较困难。此外,Maven的插件系统和概念也比较多,需要花费一定时间来学习。
- 配置复杂:虽然Maven提供了许多预设的插件和约定,但项目的构建过程仍然需要进行大量的配置。这可能会导致配置错误或过度配置,影响项目的构建速度和稳定性。
- 灵活性不足:由于Maven使用了一种基于XML的配置文件来定义构建过程,因此对于一些特殊的构建需求可能难以满足。此外,Maven的插件系统也可能限制了某些项目的定制化需求。
3.2 Gradle的优点和缺点?
Gradle是一个灵活的、功能强大的自动化构建工具,用于管理项目的构建、报告和测试。以下是Gradle的一些优点和缺点:
优点:
- 声明式构建:Gradle采用声明式构建方式,使得构建脚本更加清晰、易读和维护。
- 强大的依赖管理:Gradle具有强大的依赖管理功能,能够自动下载和管理项目所需的依赖项,减少了手动配置的繁琐。
- 灵活性:Gradle具有高度的灵活性,允许开发者根据项目需求自定义构建过程,例如添加插件、配置任务等。
- 跨平台兼容性:Gradle可以在不同的操作系统和环境中运行,具有较好的跨平台兼容性。
- 丰富的插件生态系统:Gradle拥有庞大的插件生态系统,可以支持各种开发语言和工具。
缺点: - 学习曲线:相较于一些传统的构建工具(如Apache Ant、Apache Maven),Gradle的使用需要一定的学习成本。虽然它的语法相对简单,但对于初学者来说,理解其构建逻辑和概念可能需要一些时间。
- 配置繁琐:虽然Gradle提供了丰富的配置选项和功能,但过度配置可能会使构建脚本变得复杂且难以维护。
- 兼容性问题:尽管Gradle具有良好的跨平台兼容性,但在某些情况下,特定平台的特性可能无法得到完全支持。
- 社区支持:虽然Gradle的插件生态系统相对丰富,但对于一些非主流语言或工具的支持可能不够完善。
总体而言,Gradle是一个强大且灵活的构建工具,适用于各种规模的项目。在选择使用Gradle时,需要考虑学习曲线、配置复杂性以及社区支持等因素。
3.3 Maven的坐标概念是什么?
在Maven中,坐标是一个重要的概念,用于唯一标识一个项目或依赖。Maven坐标主要由三个部分组成:groupId、artifactId和version。
groupId:通常表示项目所属的组织或公司,它以反向域名的方式开头,用于在Maven中唯一标识一个组织。例如,org.apac是Apache Software Foundation的groupId。
artifactId:是项目的名称,通常与源代码库的名称相一致。在定义项目坐标时,artifactId是必需的。
version:表示项目的版本号。每个项目可以有多个版本,通过不同的version号进行区分。在定义依赖关系时,通常需要指定依赖的version号。
除了groupId、artifactId和version,Maven坐标还可以包括其他一些元素,如packaging、scope等,用于更详细地描述项目或依赖的属性。
总之,Maven坐标通过唯一的标识符,将项目和依赖进行组织和管理,方便构建、发布和分享。
3.4 为什么选择Maven构建项目?
选择Maven构建项目的原因有很多,以下是一些主要的理由:
- 简化构建过程:Maven简化了开发人员的工作,屏蔽了很多细节,使得开发人员可以专注于业务逻辑,而不是底层细节。
- 统一管理依赖:Maven能够快速实现依赖的配置,从远程仓库中自动下载对应的依赖,管理依赖版本和解决依赖冲突,减少了手动管理的繁琐。
- 提供丰富的插件支持:Maven拥有庞大的插件生态系统,支持各种开发语言和工具,使得项目构建更加方便和快速。
- 自动化构建和测试:Maven能够自动化编译、测试、打包和部署项目,提高了构建的可靠性和效率。
- 标准化项目管理:Maven提供了一套标准的项目结构和构建流程,使得项目更加易于维护和管理。
- 跨平台兼容性:Maven可以在不同的操作系统和环境中运行,具有较好的跨平台兼容性。
- 丰富的项目信息管理:Maven提供了大量的项目信息,包括POM文件中的版本号、插件、依赖等,这些信息有助于项目成员更好地理解和使用代码。
- 最佳实践指南:Maven会汇总当前最佳开发实践的原则,并通过Maven影响其他项目。
- 透明化更新:Maven为Maven客户端提供了一种安装更新的简单方法,开发人员可以简单便捷的利用Maven的最新特性。
综上所述,选择Maven构建项目可以带来许多好处,包括简化构建过程、统一管理依赖、自动化构建和测试、标准化项目管理等。因此,Maven成为了一个流行的Java项目管理和构建工具。
3.5 为什么选择Gradle构建项目?
选择Gradle构建项目的原因有很多,以下是一些主要的理由:
- 先进的构建系统:Gradle是一个先进的构建系统,具有灵活性和可扩展性,支持自动化构建和测试,使得构建过程更加可靠和高效。
- 基于DSL的配置语言:Gradle使用一种基于Groovy的领域特定语言(DSL)来描述和处理构建逻辑,使得构建脚本更加清晰、易读和维护。
- 强大的依赖管理:Gradle能够自动下载和管理项目的依赖项,减少了手动配置的繁琐,提高了构建的一致性和可靠性。
- 灵活的插件机制:Gradle支持插件机制,开发者可以根据项目需求自定义构建过程,支持各种开发语言和工具,提高了构建的灵活性和扩展性。
- 跨平台兼容性:Gradle可以在不同的操作系统和环境中运行,具有较好的跨平台兼容性,使得项目在不同的平台上能够得到一致的构建结果。
- 社区支持:Gradle拥有庞大的社区支持,有大量的插件和资源可供参考和使用,可以帮助开发者快速解决问题和实现项目需求。
- 易于集成其他工具和框架:Gradle可以轻松地与其他工具和框架集成,例如CI/CD流水线、版本控制系统等,提高了项目管理和协作的效率。
- 强大的测试支持:Gradle对测试提供了强大的支持,包括单元测试、集成测试和端到端测试等,可以帮助开发者确保代码的质量和可靠性。
- 可定制的构建生命周期:Gradle允许开发者自定义构建生命周期的阶段和任务,以满足项目的特殊需求。
- 强大的性能优化能力:Gradle提供了性能优化相关的功能和插件,可以帮助开发者提高应用程序的性能和响应能力。
综上所述,选择Gradle构建项目可以带来许多好处,包括先进的构建系统、基于DSL的配置语言、强大的依赖管理、灵活的插件机制、跨平台兼容性等。因此,Gradle成为了一个流行的自动化构建工具。
Maven和Gradle包管理
解释
maven、gradle 中依赖的 scope 可以简单梳理成4个方面
- 支持自身编译
- 支持自身运行
- 支持子项目编译
- 支持子项目运行
以上4个方面可以进行各种组合(maven项目)
- 1:provided(自身编译要用,运行时不需要,因为其它平台会自动提供,如 servlet)
- 2:runtime+option:true(不参与编译,但是运行时对调用,比如方法内部使用的类就不需要参与编译),子项目可以不需要此功能时。这个组合少有
- 1、2:compile+option:true(自身编译运行都要,子项目可以不需要此功能时。这个组合少有)
- 1、3:没有
- 1、4:没有
- 2、3:runtime+sub-provided(应该没有这个搭配)
- 2、4:runtime+(sub-compile or sub-runtime)(此项目和其子项目都只在运行期用上)
- 1、2、3:compile+sub-provided(应该没有)
- 1、2、4:compile+sub-runtime(子项目编译期不需要,运行时会调用)
- 2、3、4:没有
- 1、2、3、4:compile+sub-compile
以上4个方面可以进行各种组合(gradle项目)
- 1:compileOnly 或 compileClasspath
- 2:runtimeClasspath
- 1、2:没有
- 1、3:compileOnlyApi
- 1、4:没有
- 2、3:没有
- 2、4:没有
- 1、2、3:没有
- 1、2、4:implementation(子项目编译期得不到,运行时可以调用,接口实现类!)
- 2、3、4:没有
- 1、2、3、4:api(接口)
gradle 各个 configuration 的继承关系
下图中相关解释:
compileClasspath:本项目编译期可用
runtimeClasspath:本项目运行期可用
runtimeElements:依赖传递到子模块,且只传递到子模块的 运行期
apiElements:依赖传递到子模块,且只传递到子模块的 编译期
其它:就是以上 4 个配置功能的组合了
以下是测试阶段可用的配置
testCompileClasspath:仅用于测试阶段的编译期
testRuntimeClasspath:仅用于测试阶段的运行期
其它:就是以上 2 个配置功能的组合了
Maven与Gradle依赖项scope
Maven和Gradle对依赖项的scope有所不同。在Maven世界中,一个依赖项有6种scope而gradle的scope 一直在优化调整优化。
maven scope属性
依赖范围控制哪些依赖在哪些classpath 中可用,哪些依赖包含在一个应用中。
-
compile (编译)
compile是默认的范围;如果没有提供一个范围,那该依赖的范围就是编译范围。编译范围依赖在所有的classpath中可用,同时它们也会被打包。 -
provided (已提供)
provided 依赖只有在当JDK 或者一个容器已提供该依赖之后才使用。例如, 如果你开发了一个web 应用,你可能在编译 classpath 中需要可用的Servlet API 来编译一个servlet,但是你不会想要在打包好的WAR 中包含这个Servlet API;这个Servlet API JAR 由你的应用服务器或者servlet 容器提供。已提供范围的依赖在编译classpath (不是运行时)可用。它们不是传递性的,也不会被打包。 -
runtime (运行时)
runtime 依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如,你可能在编译的时候只需要JDBC API JAR,而只有在运行的时候才需要JDBC
驱动实现。 -
test (测试)
test范围依赖 在一般的编译和运行时都不需要,它们只有在测试编译和测试运行阶段可用。 -
system (系统)
system范围依赖与provided类似,但是你必须显式的提供一个对于本地系统中JAR文件的路径。这么做是为了允许基于本地对象编译,而这些对象是系统类库的一部分。这样的构建应该是一直可用的,Maven 也不会在仓库中去寻找它。如果你将一个依赖范围设置成系统范围,你必须同时提供一个systemPath元素。注意该范围是不推荐使用的(建议尽量去从公共或定制的 Maven 仓库中引用依赖) -
import(导入)
import仅支持在中的类型依赖项上。它表示要在指定的POM 部分中用有效的依赖关系列表替换的依赖关系。该scope类型的依赖项实际上不会参与限制依赖项的可传递性。
gradle 依赖管理 scope(gradle 6.6.1)
-
compile(已弃用)
编译时间依赖性。被取代implementation。 -
implementation 延伸 compile
implementation 默认的scope。implementation的作用域会让依赖在编译和运行时均包含在内,但是不会暴露在类库使用者的编译时。举例,如果我们的类库包含了gson,那么其他人使用我们的类库时,编译时不会出现gson的依赖。 -
compileOnly
仅编译时依赖项,在运行时不使用。编译时可见 -
compileClasspath 延伸 compile, compileOnly, implementation
编译类路径,在编译源代码时使用。由任务使用compileJava。 -
annotationProcessor
编译期间使用的注释处理器。 -
runtime(不建议使用)扩展 compile
运行时依赖项。被取代runtimeOnly。 -
runtimeOnly
仅运行时依赖项,运行时可见 -
runtimeClasspath 延伸 runtimeOnly, runtime, implementation
运行时类路径包含实现的元素以及仅运行时元素。 -
testCompile(不建议使用)扩展 compile
编译测试的其他依赖项。被取代testImplementation。 -
testImplementation 延伸 testCompile, implementation
仅实现测试的依赖项。 -
testCompileOnly
其他依赖项仅用于编译测试,在运行时不使用。 -
testCompileClasspath 延伸 testCompile, testCompileOnly, testImplementation
测试编译类路径,在编译测试源时使用。由任务使用compileTestJava。 -
testRuntime(不建议使用)扩展 runtime, testCompile
仅用于运行测试的其他依赖项。被取代testRuntimeOnly。 -
testRuntimeOnly 延伸 runtimeOnly
运行时仅依赖于运行测试。 -
testRuntimeClasspath 延伸 testRuntimeOnly, testRuntime, testImplementation
用于运行测试的运行时类路径。由任务使用test。 -
archives
Artifacts (e.g. jars) produced by this project. Used by task uploadArchives. -
default 延伸 runtimeClasspath
项目依赖于此项目的默认配置。包含此项目在运行时所需的工件和依赖项
下图分别显示了main和test 的依赖项配置。您可以使用此图例解释颜色:
-
灰色文本-已弃用配置。
-
绿色背景-您可以声明对配置的依赖关系。
-
蓝灰色背景-该配置供任务使用,而不供您声明依赖项。
-
浅蓝色背景与等宽线文本— —一个任务。
图一 java插件- main 依赖项配置
图二 Java插件- test依赖项配置
1、依赖的方式
Gradle 中的依赖分别为直接依赖,项目依赖,本地 jar 依赖。
在Gradle中,依赖管理是一个非常重要的部分,它允许你指定项目所需的各种库和模块。你的案例中提到了三种常见的依赖类型:项目依赖、本地JAR依赖和远程仓库的直接依赖。下面我将分别解释这三种依赖类型,并提供相应的配置方法。
1.1 直接依赖(远程仓库依赖)
直接依赖指的是从远程仓库(如Maven Central)中获取的依赖。在Gradle中,你可以直接通过坐标来声明这些依赖,如下所示:
dependencies {
// 直接依赖的简写形式
implementation 'cn.hutool:hutool-all:5.8.27'
// 直接依赖的完整形式
iimplementation group: 'cn.hutool', name: 'hutool-all', version: '5.8.27'
}
在Maven的pom.xml
文件中,这相当于:
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.27</version>
</dependency>
</dependencies>
在Gradle中,implementation
是依赖配置之一,它相当于Maven中的compile
作用域。
1.2 项目依赖
项目依赖是指在一个多模块项目中,一个模块依赖于另一个模块。在Gradle中,你可以使用project
方法来声明这种依赖,如下所示:
dependencies {
// 项目依赖
implementation project(':subject01')
}
在settings.gradle
文件中,你需要配置子模块的路径,以便Gradle能够识别它们:
rootProject.name = 'root'
include 'subject01' // 包含名为subject01的子模块
1.3 本地JAR依赖
本地JAR依赖指的是直接引用项目目录中的JAR文件。在Gradle中,你可以使用files
方法或fileTree
方法来声明这些依赖:
dependencies {
// 直接依赖特定的JAR文件
implementation files('libs/foo.jar', 'libs/bar.jar')
// 配置一个文件夹作为依赖项,自动包含该文件夹下的所有JAR文件
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
使用fileTree
方法时,Gradle会自动包含指定目录下的所有JAR文件作为依赖。
总结
- 直接依赖:从远程仓库获取的依赖,使用坐标声明。
- 项目依赖:在一个多模块项目中,一个模块对另一个模块的依赖。
- 本地JAR依赖:直接引用本地文件系统中的JAR文件。
每种依赖类型都有其适用场景,你可以根据项目的具体需求选择合适的依赖管理方式。
2、依赖的下载
当执行 build 命令时,gradle 就会去配置的依赖仓库中下载对应的 Jar,并应用到项目中。
3、依赖的类型
类似于 Maven 的 scope 标签,gradle 也提供了依赖的类型,具体如下所示:
配置选项 | 描述 | 适用插件 |
---|---|---|
compileOnly |
编译时需要但不打包的依赖。曾短暂称为provided 。 |
Java |
runtimeOnly |
运行时需要,编译时不需要的依赖,如数据库驱动。 | Java |
implementation |
编译和运行时都需要的依赖。 | Java |
testCompileOnly |
仅用于编译测试的依赖,运行时不需要。 | Java |
testRuntimeOnly |
仅在测试运行时需要的依赖。 | Java |
testImplementation |
针对测试代码的依赖,取代老版本中的testCompile 。 |
Java |
providedCompile |
WAR插件专用,编译和测试需要,运行时由容器提供。 | WAR |
compile |
已在Gradle 7.0中移除,原表示编译时和打包时都需要的依赖。 | Java (已移除) |
runtime |
已在Gradle 7.0中移除,原表示运行和测试时需要的依赖。 | Java (已移除) |
api |
编译时和运行时都需要的依赖,并且可以被使用者传递性地导出。 | java-library |
compileOnlyApi |
仅在编译时需要的依赖,运行时不需要,可以被使用者传递性地导出。 | java-library |
请注意,compile
和runtime
配置选项在Gradle 7.0中已经被移除,推荐使用implementation
和runtimeOnly
作为替代。此外,api
和compileOnlyApi
是java-library
插件提供的,用于更细粒度地控制模块间的依赖关系。
官方文档参考:
https://docs.gradle.org/current/userguide/java_library_plugin.html#java_library_plugin: 各个依赖范围的关系和说明
https://docs.gradle.org/current/userguide/upgrading_version_6.html#sec:configuration_removal : 依赖范围升级和移除
https://docs.gradle.org/current/userguide/java_library_plugin.html#java_library_plugin:API 和 implemention 区别
https://docs.gradle.org/current/userguide/java_plugin.html#java_plugin: 执行 java 命令时都使用了哪些依赖范围的依赖。
提示:java 插件提供的功能,java-library 插件都提供
4、api 与 implementation 区别
以下是您提供的关于Gradle依赖配置选项api
和implementation
的比较表格,我对其进行了优化和整理:
特性/场景 | api 配置选项 |
implementation 配置选项 |
---|---|---|
编译时 | - 依赖可以传递给模块的使用者。 | - 依赖不会传递给模块的使用者。 |
- 当底层依赖发生变化时,所有依赖了这些底层依赖的模块都需要重新编译,可能导致编译速度变慢。 | - 当底层依赖发生变化时,只有直接依赖了这些底层依赖的模块需要重新编译,编译速度相对较快。 | |
运行时 | - 所有模块的类都会被加载。 | - 所有模块的类都会被加载。 |
应用场景 | - 适用于多模块的项目,特别是当你想要避免重复依赖同一个模块时。 | - 在大多数情况下使用,尤其是当你不希望依赖传递给模块使用者时。 |
api
和implementation
是Gradle中常用的两种依赖配置选项,它们在编译时和运行时的行为有所不同。api
配置选项允许依赖传递,这在多模块的项目中非常有用,可以确保模块间的依赖关系一致性。而implementation
配置选项则不会将依赖传递给使用者,这在大多数情况下是推荐使用的,因为它可以减少不必要的依赖传递,从而提高项目的构建效率。
以下是对这两种依赖类型的详细解释,以及它们在编译时和运行时的不同影响:
4.1 api 依赖
-
编译时:当一个库(如 libC)被声明为
api
依赖时,它的内容变化会导致所有直接和间接依赖它的项目(如 libA 和 projectX)都需要重新编译。这是因为api
依赖的变更可能会影响所有使用该 API 的代码。 -
运行时:在运行时,所有通过
api
依赖的库(如 libC、libA)以及最终的应用程序(如 projectX)中的类都会被类加载器加载。 -
适用场景:
api
适用于多模块项目中的依赖管理,特别是当你想避免重复依赖时。例如,如果 moduleA 依赖 moduleB,而 moduleB 又依赖 moduleC,同时 moduleA 也需要 moduleC 中的某些类或方法,你可以将 moduleC 作为api
依赖添加到 moduleB 中。这样,moduleA 只需要依赖 moduleB,而 moduleC 的依赖会被传递。
4.2 implementation 依赖
-
编译时:使用
implementation
依赖时,依赖的传递性会被限制。如果一个库(如 libD)被声明为implementation
依赖,并且它的内容发生变化,只有直接依赖它的库(如 libB)需要重新编译。不依赖于 libD 的项目(如 libA 和 projectX)不需要重新编译,这可以加快编译速度。 -
运行时:尽管在编译时
implementation
依赖不会被传递,但在运行时,所有通过implementation
依赖引入的库(如 libD、libB)以及最终的应用程序(如 projectX)中的类都会被加载。 -
适用场景:
implementation
适用于那些不应该被其他模块或应用程序直接使用的库。它通常是内部实现细节,不是用来暴露公共 API 的。
4.3 拓展
api 和 implementation 案例分析 :
在多模块项目中,使用 api
和 implementation
可以有效地管理模块之间的依赖关系:
-
使用
api
:当你希望一个模块的依赖成为另一个模块的公共 API 时,使用api
。这样,任何依赖于该模块的项目都能够访问到这些 API。 -
使用
implementation
:当你希望隐藏一个模块的实现细节,只将必要的功能暴露给依赖它的模块时,使用implementation
。这有助于减少编译时的依赖传递,提高构建效率。
总之,除非涉及到多模块依赖,为了避免重复依赖,咱们会使用api,其它情况我们优先选择implementation,拥有大量的 api依赖项会显著增加构建时间。
5、依赖冲突及解决方案
依赖冲突是指 “在编译过程中, 如果存在某个依赖的多个版本, 构建系统应该选择哪个进行构建的问题”,如下所示:
A、B、C 都是本地子项目 module,log4j 是远程依赖。
编译时: B 用 1.4.2 版本的 log4j,C 用 2.2.4 版本的 log4j,B 和 C 之间没有冲突
打包时: 只能有一个版本的代码最终打包进最终的A对应的 jar 或 war包,对于 Gradle 来说这里就有冲突了
5.1 案例演示:
我们在 build.gradle 引入依赖库
修改 build.gradle
如上所示:默认下,Gradle 会使用最新版本的 jar 包【考虑到新版本的 jar 包一般都是向下兼容的】,实际开发中,还
是建议使用官方自带的这种解决方案。当然除此之外,Gradle 也为我们提供了一系列的解决依赖冲突的方法: exclude
移除一个依赖,不允许依赖传递,强制使用某个版本。
5.2 Exclude 排除某个依赖
5.3 不允许依赖传递【一般不用】
在添加依赖项时,如果设置 transitive 为 false,表示关闭依赖传递。即内部的所有依赖将不会添加到编译和运行时的类路径。
5.4 强制使用某个版本【官方建议】
拓展
在 Gradle 中,如果你想要避免依赖冲突并确保构建的可预测性,你可以配置构建过程在遇到依赖冲突时立即失败。这可以帮助你快速发现并解决版本不一致的问题。
以下是如何配置 Gradle 在遇到依赖冲突时立即失败的示例:
configurations.all {
resolutionStrategy.failOnVersionConflict()
}
这段代码应该放在你的 build.gradle
文件中的项目配置部分。通过使用 configurations.all
方法,你可以为项目中所有的配置应用这个策略。resolutionStrategy.failOnVersionConflict()
会让 Gradle 在解析依赖时,如果遇到任何版本冲突,就会立即停止构建并报告错误。
依赖冲突检查的好处
-
及时发现问题:构建失败提供了一个明确的信号,表明依赖之间存在不兼容的版本,这可以防止不兼容的依赖被无意中包含进构建中。
-
简化调试:立即失败可以简化调试过程,因为你不需要去猜测为什么构建成功但运行时却出现问题。
-
避免运行时错误:通过确保所有依赖都是兼容的,可以减少运行时由于依赖不匹配导致的意外错误。
注意事项
-
在团队协作中,这个策略可以帮助每个成员都意识到依赖版本的重要性。
-
对于大型项目或有复杂依赖关系的情况,这个策略可能会导致频繁的构建失败,因此可能需要配合其他依赖管理策略使用。
-
在实际操作中,可能需要结合项目的具体需求和依赖管理策略来决定是否使用这个选项。
通过这种方式,你可以更好地控制项目的依赖,确保依赖的一致性和项目的稳定性。
Gradle 进阶学习 之 build.gradle 文件-CSDN博客
到这里我们先来熟悉下 Gradle(4.1以上版本) 的构建文件中添加依赖的几种方式:
- api:与旧版的compile相同;
- implementation:解决重复依赖问题。例如A依赖B,B依赖C,那么A里面将不能调用C的方法;
- compileOnly:编译有效,打包无效。替代旧版的 provider;
- runtimeOnly:打包有效,编译无效;
- test、debug、release implementation:对应test、debug、release环境的implementation;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
2022-01-25 linux下nproc的作用,Linux句柄调优之nofile、nr_open、file-max
2018-01-25 [No0000119]什么是柳比歇夫的时间事件记录法