阿里云能放企业网站吗,网站做优化一般几个字,湖南长沙设计公司,在什么网站可以接设计做文章目录 1.概述2.依赖管理2.1.坐标2.2.依赖的基本概念2.3.依赖配置#xff08;Dependency configurations#xff09;2.3.1.依赖路径2.3.2.依赖配置与依赖路径的关联 2.4.依赖传递2.4.1.准备工作2.4.2.运行时依赖传递jar包生成与依赖配置依赖树打印使用 Dependency Analyzer… 文章目录 1.概述2.依赖管理2.1.坐标2.2.依赖的基本概念2.3.依赖配置Dependency configurations2.3.1.依赖路径2.3.2.依赖配置与依赖路径的关联 2.4.依赖传递2.4.1.准备工作2.4.2.运行时依赖传递jar包生成与依赖配置依赖树打印使用 Dependency Analyzer 查看依赖 2.4.3.编译时依赖传递 2.5.依赖冲突2.5.1.依赖解析dependency resolution版本号的组成版本号排序规则 2.5.2.排除传递依赖单个排除全局排除 2.5.3.强制指定版本号 3.总结 1.概述
在前面两篇文章中我们已通过 Gradle 创建了一个多模块的项目并介绍了一些常用的核心概念。至此我们已能够在工作中使用 Gradle 了。
本篇内容主要讲解 Gradle 的依赖管理旨在让大家对 Gradle 的依赖特性有一定了解能够处理工作中出现的一系列依赖冲突问题包含以下内容
讲解坐标的概念介绍依赖范围的概念及选择说明依赖如何传递、如何避免依赖传递阐述依赖冲突的概念、如何确认依赖冲突、如何解决依赖冲突 Gradle历史文章 《【Gradle】一在IDEA中初始化gradle项目及其核心概念解释》 《【Gradle】二多模块项目、api推送到Maven私服、及SpringBoot可执行Jar包》 Maven相关文章 《Maven中的依赖功能特性》 《Maven坐标的概念与规则》
对Maven感兴趣的话可以看看我的上面两篇Maven文章里面也介绍了一些依赖的概念当然如果不感兴趣的话本篇文章也会引用这两篇Maven中的部分内容不影响本篇文章的阅读。
2.依赖管理
2.1.坐标
在 Gradle 中依赖的版本控制通常基于 Maven 的坐标系统使用组 IDgroupId、构件 IDartifactId和版本号version来唯一标识一个依赖项这被称为 GAV坐标。 例如implementation cn.hutool:hutool-all:5.8.18 其中 cn.hutool 是组 IDhutol-all是构件 ID5.8.18 是版本号。
Gradle的构建一般也是使用的Maven仓库很多时候在仓库中的jar包是共用的所以我们在编写坐标时也应遵循Maven的规范
groupId指的是当前构建隶属的实际项目一般是 公司的网址倒序 项目名artifactId一般是指的当前项目中的其中一个模块version当前项目的版本号 版本号也不是随便写的在业界有通用的规则定义方式如下
{主版本号}.{次版本号}.{增量版本号}-{里程碑版本}主版本号一般是指的当前的项目有了重大的架构变动版本之间几乎完全不兼容例如最近出的 SpringBoot3 就已经放弃了Java8如果不升级 JDK的话还是只能使用SpringBoot2次版本号一般是指的项目的迭代版本这种版本会修复大量的bug带来一些小的新特性等但是没有什么架构上的重大变化。增量版本号一般是用于修复一些紧急bug改动量很小时可以使用增量版本号。里程碑版本就是项目的当前版本处于一个什么样的阶段了常见的里程碑版本有 SNAPSHOTalphabetareleaseGA 等。 在里程碑的版本中标注SNAPSHOT的为开发版此时会存在大量的代码变动alpha和beta分别对应的是内测版与公测版这三个版本都是属于不稳定版本使用的时候非常容易踩坑所以一般只用的demo体验在正式环境中不能使用。 release和GA都属于是稳定的正式版本可以在正式环境中使用。
下面是SpringBoot的一些版本号可以体验一下
2.2.依赖的基本概念
在软件开发中依赖是指一个项目或模块对其他项目或模块的需求。例如如果一个项目需要使用某个第三方库来实现某些功能那么这个项目就对该第三方库存在依赖。
假设现在有 A、B、C 三个构件如下图中表示的就是A依赖B和C 通过坐标定义
dependencies {implementation com.ls:B:1.0.0implementation com.ls:C:1.0.0
}2.3.依赖配置Dependency configurations
在这个章节会了解到Gradle中的几种常用的依赖路径以及依赖配置和依赖范围的对应关系《官方参考文档》
2.3.1.依赖路径
从宏观的角度来看常用的依赖类型有两种编译路径CompileClasspath和运行时路径RuntimeClasspath在这两个概念的基础上又加入了测试代码和非测试代码的区分概念如下
CompileClasspath编译类路径在编译项目时需要的依赖路径。RuntimeClasspath运行时类路径在运行项目时需要的依赖路径。TestCompileClasspath测试代码编译类路径是在编译测试代码时所需的依赖路径。TestRuntimeClasspath测试代码运行时类路径用于运行测试代码时所需的依赖路径。
除此之外还有一种特殊的路径annotationProcessor它也是一种依赖配置在编译时生效。主要是用于将某些注解添加到依赖路径中启用对注解的支持。在上篇文章中提到的lombok的注解支持就是这种类型
dependencies {compileOnly org.projectlombok:lombok:$lombokVersionannotationProcessor org.projectlombok:lombok:$lombokVersion
}说的更直白一点所谓的编译时路径就是写代码的时候可以直接Import使用的路径运行时路径就是写代码的时候无法引入但在程序运行的时候能够访问到常见于间接依赖中。
2.3.2.依赖配置与依赖路径的关联
implementation添加直接依赖包到编译时和运行时环境api添加直接依赖包和间接依赖包到编译时和运行时环境即向上层传递内部依赖关系compileOnly添加依赖包不添加到运行时路径runtimeOnly 不添加到编译时路径添加运行时路径annotationProcessor添加到注解处理器的类路径不添加到运行时路径
更多依赖配置对应的依赖路径关系如下表所示
依赖配置Compile ClasspathRuntime Classpathapi√√implementation√√compileOnlyApi√×compileOnly√×runtimeOnly×√testImplementation√√testCompileOnly√×testRuntimeOnly×√annotationProcessor√×
在上面的表格中有test前缀的依赖配置只会在test任务中生效。 此外可以看到还多了一个api这也是一个可能会使用到的功能主要是用在依赖传递中在下面会讲解。
2.4.依赖传递
我们上面提到了编译时和运行时两种依赖路径依赖传递也分为编译时依赖传递和运行时依赖传递其中编译时依赖传递还依赖于api这个依赖配置要使用这个配置还需要额外引入java-library插件。下面就通过一个demo案例来体验一下两种依赖传递同时会介绍IDEA中自带的一种依赖查看工具 Dependency Analyzer。
2.4.1.准备工作
需要创建3个子模块进行模拟分别是a-demo,b-demo,c-demo同时需要将它们打包到本地Maven仓库中。
创建子模块 创建3个子模块并修改每个子模块的build.gradle里面只放入version 1.0.0其他配置由根项目进行配置。
根项目配置 根项目中需要配置的有三个插件配置、仓库配置、发布配置配置的含义在本系列的前两篇文章中已经讲过了这里就不再赘述了根项目完整的配置如下
plugins {id javaid org.springframework.boot version $springBootVersion apply falseid io.spring.dependency-management version $springDenpendencyVersion apply false
}allprojects {repositories {mavenLocal()maven {url https://maven.aliyun.com/repository/public}mavenCentral()}
}subprojects {apply plugin: javaapply plugin: maven-publishapply plugin: java-librarysourceCompatibility JavaVersion.VERSION_1_8java {withSourcesJar()}publishing {publications {demo(MavenPublication) {from components.java}}repositories {maven {name mavenNexusallowInsecureProtocol truedef releasesRepoUrl http://192.168.200.101:8081/repository/maven-releases/def snapshotsRepoUrl http://192.168.200.101:8081/repository/maven-snapshots/url version.endsWith(SNAPSHOT) ? snapshotsRepoUrl : releasesRepoUrlcredentials {username adminpassword admin123}}}}dependencies {compileOnly org.projectlombok:lombok:$lombokVersionannotationProcessor org.projectlombok:lombok:$lombokVersion}}gradle.properties配置 资源文件中需要配置项目整体的groupId和默认的version。
groupcom.ls
version1.0.02.4.2.运行时依赖传递
jar包生成与依赖配置
要感受到依赖传递需要依次对c-demob-demo 两个依赖打包首先是对c进行打包和推送到本地Maven仓库。
推送完成后修改b-demo的build文件再按同样的方式打包
最后在a-demo中引入b-demo并刷新Gradle引用。
依赖树打印
这里有两种方式可以查看依赖关系一种是gradle自带的依赖树打印功能gradlew dependencies另一种则是使用IDEA中的 Dependency Analyzer工具。
我们先看看第一种方式要看a-demo的依赖关系首先打开终端控制台进入a-demo模块然后再利用根目录的gradlew文件执行打印依赖树指令
cd a-demo
../gradlew dependencies在上图中我们可以看到编译时路径和运行时路径 的依赖关系。
编译时路径中 有 b-demo 和 lombok其中 b-demo 的依赖配置是 implementation上面提到了这种依赖配置会将jar包添加到编译时和运行时两种依赖路径中,lombok 是在根项目中配置的compileOnly每个子模块都继承了这个配置所以可以看到在编译时路径中会存在也正是因为在编译时存在在代码中才可以直接使用。 可以看到的是目前的配置中编译时并没有发生依赖传递即没有出现间接依赖。运行时路径中 我们可以看到的是在运行时路径中除了b-demo这个直接依赖以外还有c-demo这个间接依赖这是因为 implementation 可以实现包在运行时路径上传递。
使用 Dependency Analyzer 查看依赖
如果觉得上面这种打印依赖树的方式有点繁琐还可以使用IDEA的自带工具好像是2022以上版本就有了。如下图可以直观的看到编译时和运行时的依赖关系和上面打印出的结果是一致的。 如果想看到更详细的信息可以选择红框中的任意一个包右键选择Analyze Dependencies进入到工具弹窗中。
通过这里的一系列操作就可以查看到自己想看的依赖关系了举个例子假如我现在想看a-demo的运行时依赖关系通过树状展示可以这么做。
2.4.3.编译时依赖传递
编译时依赖传递需要通过api这个依赖配置实现通过java-library插件引入。在根项目的subprojects中已经设置了apply plugin: java-library这里可以直接使用。
现在我希望将b-demo中引入的c-demo包在编译时和运行时都通过通过间接依赖的形式传递给a-demo只需要修改B模块中的build文件将 implementation 修改为 api修改完成后重新打包。在a-demo里面引入b。
b-demo:version 1.0.1dependencies {api com.ls:c-demo:1.0.0
}a-demo:version 1.0.0dependencies {implementation com.ls:b-demo:1.0.1
}最后的结果是两种路径的依赖都传递了
2.5.依赖冲突
简单的说依赖冲突就是在一个项目中引入了 groupId 和 ArtifactId 一样但 version 不一样的jar包程序在使用时不能确定要使用哪一个包。 《依赖解析官网文档》
2.5.1.依赖解析dependency resolution
不管是Maven还是Gradle都有处理依赖冲突的“调解机制”在Gradle中叫“依赖解析dependency resolution”机制两者之间还是有一定的区别在工作中需要注意不能混为一谈。
Maven的处理方式是通过“就近原则”即选择依赖路径中最近的那个版本详情可以看这篇文章《Maven坐标的概念与规则》Gradle使用的是“最新版本优先”策略在有多个版本冲突时Gradle 默认选择最高的版本。 在判断多个版本的高低时是通过定义好的版本排序机制来处理的如果想对这个排序机制有比较详细的了解可以去看看《版本排序官方文档》下面先聊一下版本号的组成规则再解释一下这个排序机制。
版本号的组成
版本号实际上是由两部分组成的BaseVersionQualifier。
BaseVersion基础版本号规范的情况下由 x.y.z 3个数字组成但也有其他字母组成的情况。Qualifier限定符规范的情况下有beta,SNAPSHOT,RELEASE,GA等也可能是其他不规则的符号或者没有限定符。
下面的官网提供的例子如何分解基础版本号与限定符 在版本号里面有.,,-,_这样的分隔符以及字母和数字之间有一个空串分隔符出现这些分隔符的时候会找第一个不是.的分隔符左侧就是基础版本号右侧就是限定符。
版本号排序规则
第一步通过分隔符将版本号拆分成不同的组成部分 通过.,,-,_这几个符号进行拆分同时如果有字母和数字的组合也会拆解成不同的部分。 例如1a11.a.11-a-1 不管分隔符是什么都会拆分成[1,a,1]三个部分。第二步分别对比每个部分的值 就像字符串的对比那样从左到右一个一个的对比其大小规则为2 1 0 b a B Am遇到多个部分组合比较的时候还要注意一个规则1.1.0 1.1 1.1.a第三步特殊含义的限定符比较 比较规范的版本号中右侧的限定符一般是用来标识当前的jar包状态的例如是开发状态还是发布状态是测试版还是稳定版等等。 这种情况也有优先级顺序1.0-dev 1.0-alpha 1.0-zeta 1.0-rc 1.0-snapshot 1.0-final 1.0-ga 1.0-release 1.0-sp 1.0
上面的规则比较多不太方便记忆但是也没关系在大多数情况下版本号都是比较规范的只需要比较数字的大小就可以了。 2.5.2.排除传递依赖
我们在引入多种三方jar包的时候通过依赖传递的特性可能会间接引入了某一个jar包的多个版本但我们并不想使用Gradle自动指定的最高版本这时候我们就可以通过排除传递依赖的方式把最高版本的包排除掉。《排除传递依赖官方文档》
单个排除
语法也比较简单在implementation引入依赖的时候通过exclue排除依赖就可以了需要将语法由空格隔开调整为用小括号包裹。exclue的参数常用的有4种组合分别对应不同的功能 排除所有传递依赖 artifactId在exclue的参数中使用module标记。 implementation(com.ls:demo-api:1.0.0) {exclude(group: *)
}implementation(com.ls:demo-api:1.0.0) {exclude(module: *)
}指定排除某个groupId的依赖 implementation(com.ls:demo-api:1.0.0) {exclude(group: org.slf4j)
}指定排除某个构件 implementation(com.ls:demo-api:1.0.0) {exclude(module: log4j)
}指定排除某个groupId下的某个构件依赖 implementation(com.ls:demo-api:1.0.0) {exclude group: com.ls, module: c-demo
}全局排除
除了一个一个的排除以外有时候我们还想一次排除项目中的所有指定的传递依赖例如前两年的log4j包出现了安全问题需要把它从全局排除掉。
以上面多个子模块的demo项目为例先查依赖找到log4j包
// 根项目的 build.gradle 文件
subprojects {configurations.configureEach {exclude group: org.apache.logging.log4j, module: log4j-to-slf4jexclude group: org.apache.logging.log4j, module: log4j-api}
}说明configurations.configureEach 表示在所有的依赖配置中都排除掉是Gradle6以后得功能旧版本里面可以使用configurations.all排除后的结果如下图。 如果只想排除一定的依赖配置可以通过下面的语法来实现
// 根项目的 build.gradle 文件
subprojects {configurations {compileOnly.exclude group: org.apache.logging.log4j, module: log4j-to-slf4jimplementation.exclude group: org.apache.logging.log4j, module: log4j-api}
}2.5.3.强制指定版本号
可以通过force来强制使用某个版本号这个功能也是在configurations 中进行配置平时使用的不多这里就简单提一下
configurations.all {resolutionStrategy {force com.ls:c-demo:1.0.1}
}指定了之后就不会管依赖传递中c-demo的版本号了通通使用1.0.1版本。
3.总结
本文着重针对 Gradle 的依赖管理展开了全方位且极为详尽的介绍。在此过程中清晰明确地提及了诸如坐标、依赖的基础概念、依赖配置、依赖传递还有依赖冲突等相关特性。在充分熟知并掌握了这些特性之后于后续开展的工作当中就能够更加精确、细致地对依赖进行管理并且可以妥善、有效地处理由依赖冲突所引发的各种各样的问题。