依赖项最佳实践
使用版本目录集中管理依赖项版本
使用版本目录提供了一种集中式的声明式方式来管理构建中的依赖项版本。
解释
当您在单一的共享版本目录中定义依赖项版本时,可以减少重复并简化升级。您只需在一个地方更新版本,而不必更改数十个 build.gradle(.kts)
文件。这简化了维护,提高了代码一致性,并降低了模块之间意外版本偏差的风险。跨项目的一致版本声明也使得在测试期间更容易推断行为——尤其是在模块化构建中,传递性升级可能会在构建后期悄无声息地改变运行时行为。
示例
不要这样做
避免在 project.ext
、常量或局部变量中声明版本
[versions]
groovy = "3.0.5"
junit-jupiter = "5.10.0"
[libraries]
groovy-core = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
groovy-json = { module = "org.codehaus.groovy:groovy-json", version.ref = "groovy" }
groovy-nio = { module = "org.codehaus.groovy:groovy-nio", version.ref = "groovy" }
commons-lang3 = { group = "org.apache.commons", name = "commons-lang3", version = { strictly = "[3.8, 4.0[", prefer="3.9" } }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" }
[bundles]
groovy = ["groovy-core", "groovy-json", "groovy-nio"]
[plugins]
versions = { id = "com.github.ben-manes.versions", version = "0.45.0" }
plugins {
id("java-library")
alias(libs.plugins.versions)
}
dependencies {
api(libs.bundles.groovy)
testImplementation(libs.junit.jupiter)
implementation(libs.commons.lang3)
}
plugins {
id('java-library')
alias(libs.plugins.versions)
}
dependencies {
api(libs.bundles.groovy)
testImplementation(libs.junit.jupiter)
implementation(libs.commons.lang3)
}
避免将版本目录用于不相关的目的
-
不要用它们来存储共享字符串或非库常量
-
不要用任意逻辑或特定插件配置来过度使用它们
而是这样做
在您的 gradle/
目录中使用集中的 libs.versions.toml
文件
plugins {
id("java-library")
id("com.github.ben-manes.versions").version("0.45.0")
}
val groovyVersion = "3.0.5"
dependencies {
api("org.codehaus.groovy:groovy:$groovyVersion")
api("org.codehaus.groovy:groovy-json:$groovyVersion")
api("org.codehaus.groovy:groovy-nio:$groovyVersion")
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
}
dependencies {
implementation("org.apache.commons:commons-lang3") {
version {
strictly("[3.8, 4.0[")
prefer("3.9")
}
}
}
plugins {
id('java-library')
id('com.github.ben-manes.versions').version('0.45.0')
}
def groovyVersion = '3.0.5'
dependencies {
api("org.codehaus.groovy:groovy:$groovyVersion")
api("org.codehaus.groovy:groovy-json:$groovyVersion")
api("org.codehaus.groovy:groovy-nio:$groovyVersion")
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
implementation("org.apache.commons:commons-lang3") {
version {
strictly("[3.8, 4.0[")
prefer("3.9")
}
}
}
合理命名版本目录条目
在版本目录中使用一致且具有描述性的名称,可以提高构建脚本的可读性和可维护性。
解释
版本目录提供了一种集中管理依赖项的方式。采用清晰的目录条目命名约定,可以确保开发人员在整个项目中轻松识别和使用依赖项。
以下准则有助于有效命名目录条目
-
使用连字符分隔段落:优先使用连字符(
-
)而非下划线(_
)来分隔条目名称的不同部分。示例:对于
org.apache.logging.log4j:log4j-api
,使用log4j-api
-
从项目组派生第一段:使用项目组 ID 中的唯一标识符作为第一段。
示例:对于
com.fasterxml.jackson.core:jackson-databind
,使用jackson-databind
-
省略冗余段落:如果组 ID 和 artifact ID 相同,则避免重复它们。
示例:对于
io.ktor:ktor-client-core
,使用ktor-client-core
,而不是ktor-ktor-client-core
-
将内部连字符转换为 camelCase:如果 artifact ID 包含连字符,将其转换为 camelCase 以提高代码的可读性。
示例:
spring-boot-starter-web
变为springBootStarterWeb
-
避免隐含术语:排除在项目上下文中显而易见或隐含的术语。
示例:对于
com.amazonaws:aws-java-sdk-core
,使用aws-core
,而不是aws-javaSdkCore
-
为插件库添加
-plugin
后缀:当将插件作为库引用时(不在[plugins]
部分),在名称后附加-plugin
。示例:对于
org.owasp:dependency-check-gradle
,使用dependency-check-plugin
示例
[versions]
slf4j = "2.0.13"
jackson = "2.17.1"
groovy = "3.0.5"
checkstyle = "8.37"
commonsLang = "3.9"
[libraries]
# SLF4J
slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
# Jackson
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
jackson-dataformatCsv = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-csv", version.ref = "jackson" }
# Groovy bundle
groovy-core = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
groovy-json = { module = "org.codehaus.groovy:groovy-json", version.ref = "groovy" }
groovy-nio = { module = "org.codehaus.groovy:groovy-nio", version.ref = "groovy" }
# Apache Commons Lang
commons-lang3 = { group = "org.apache.commons", name = "commons-lang3", version = { strictly = "[3.8, 4.0[", prefer = "3.9" } }
[bundles]
groovy = ["groovy-core", "groovy-json", "groovy-nio"]
[plugins]
versions = { id = "com.github.ben-manes.versions", version = "0.45.0" }
plugins {
id("java-library")
alias(libs.plugins.versions)
}
repositories {
mavenCentral()
}
dependencies {
// SLF4J
implementation(libs.slf4j.api)
// Jackson
implementation(libs.jackson.databind)
implementation(libs.jackson.dataformatCsv)
// Groovy bundle
api(libs.bundles.groovy)
// Commons Lang
implementation(libs.commons.lang3)
}
plugins {
id 'java-library'
alias(libs.plugins.versions)
}
repositories {
mavenCentral()
}
dependencies {
// SLF4J
implementation libs.slf4j.api
// Jackson
implementation libs.jackson.databind
implementation libs.jackson.dataformatCsv
// Groovy bundle
api libs.bundles.groovy
// Commons Lang
implementation libs.commons.lang3
}
在 settings.gradle.kts
中设置依赖仓库
在 settings.gradle.kts
中声明插件和依赖项的仓库。
解释
使用 settings.gradle.kts
文件声明仓库有以下几个好处
-
避免重复:集中声明仓库消除了在每个项目的
build.gradle.kts
中重复声明的需要。 -
提高可调试性:确保所有项目在解析期间都从相同的仓库以一致的顺序解析依赖项。
-
匹配构建模型:仓库不是项目定义的一部分;它们是全局构建逻辑的一部分,因此 settings 是一个更合适放置它们的地方。
注意:尽管dependencyResolutionManagement.repositories
是一个孵化中的 API,但它是声明仓库的首选方式。
示例
不要这样做
您可以在单独的 build.gradle.kts
文件中设置仓库,使用
buildscript {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("java")
}
repositories {
mavenCentral()
}
buildscript {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("java")
}
repositories {
mavenCentral()
}
而是这样做
而是,您应该在 settings.gradle.kts
中像这样设置它们
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
repositories {
mavenCentral()
}
}
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
repositories {
mavenCentral()
}
}
不要显式依赖 Kotlin 标准库
Kotlin Gradle 插件会自动为每个源集添加对 Kotlin 标准库 (stdlib
) 的依赖,因此无需显式声明它。
解释
添加的标准库版本与应用于项目的 Kotlin Gradle 插件版本相同。如果您的构建不需要特定或不同版本的标准库,则应避免手动添加它。
将 kotlin.stdlib.default.dependency 属性设置为 false 可以阻止 Kotlin 插件自动将 Kotlin 标准库依赖项添加到您的项目中。这在特定场景下很有用,例如当您希望手动管理 Kotlin 标准库依赖项版本时。 |
示例
不要这样做
plugins {
kotlin("jvm").version("2.0.21")
}
dependencies {
api(kotlin("stdlib")) (1)
}
plugins {
id("org.jetbrains.kotlin.jvm") version "2.0.21"
}
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib:2.0.21") (1)
}
1 | 显式依赖 stdlib :此项目包含对 Kotlin 标准库的隐式依赖,这是编译其源代码所必需的。 |
而是这样做
plugins {
kotlin("jvm").version("2.0.21")
}
plugins {
id("org.jetbrains.kotlin.jvm") version "2.0.21"
}
未显式包含 stdlib
依赖项:标准库仍然可用,需要它的源代码可以正常编译。
避免重复声明依赖项
避免多次声明同一个依赖项,特别是当它已通过传递性依赖或通过其他配置可用时。
解释
在 Gradle 构建脚本中重复依赖项可能导致
-
维护成本增加:在多个地方声明依赖项会使其更难管理。
-
意外行为:在多个配置(例如
compileOnly
和implementation
)中声明同一个依赖项可能导致难以诊断的 classpath 问题。
示例
不要这样做
plugins {
`java-library`
}
dependencies {
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0") (1)
}
plugins {
id 'java-library'
}
dependencies {
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0") (1)
}
1 | 在 implementation 作用域中重复的依赖项。 |
而是这样做
plugins {
`java-library`
}
dependencies {
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0") (1)
}
plugins {
id 'java-library'
}
dependencies {
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.0") (1)
}
1 | 只声明一次依赖项 |