From 12ab8a928e1d1cc889134adc323e8472a9027d6a Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Fri, 30 Oct 2026 20:19:16 +0800 Subject: [PATCH] =?UTF-8?q?Update=20=E6=9E=84=E5=BB=BA=E5=B7=A5=E5=85=B7.m?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补充 Gradle 最小构建脚本与依赖冲突规则说明,新增实测附录;调整 Maven 仓库机制描述,补充依赖树排查示例并校对文字。 --- notes/构建工具.md | 70 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/notes/构建工具.md b/notes/构建工具.md index 63ab0e0b..24f2f9ba 100644 --- a/notes/构建工具.md +++ b/notes/构建工具.md @@ -4,6 +4,7 @@ * [一、构建工具的作用](#一构建工具的作用) * [二、Java 主流构建工具](#二java-主流构建工具) * [三、Maven](#三maven) + * [附录:实测](#附录实测) * [参考资料](#参考资料) @@ -14,15 +15,15 @@ 其中构建工具提供的依赖管理可以自动处理依赖关系。例如一个项目需要用到依赖 A,A 又依赖于 B,那么构建工具就能自动导入 B,而不需要手动去寻找并导入。 -在 Java 项目中,打包流程通常是将项目打包成 Jar 包。在没有构建工具的情况下,需要使用命令行工具或者 IDE 手动打包。而发布流程通常是将 Jar 包上传到服务器上。 +在 Java 项目中,打包流程通常是将项目打包成 jar 包。在没有构建工具的情况下,需要使用命令行工具或者 IDE 手动打包。而发布流程通常是将 jar 包上传到服务器上。 ## 二、Java 主流构建工具 -Ant 具有编译、测试和打包功能,其后出现的 Maven 在 Ant 的功能基础上又新增了依赖管理功能,而最新的 Gradle 又在 Maven 的功能基础上新增了对 Groovy 语言的支持。 +Ant 具有编译、测试和打包功能,其后出现的 Maven 在 Ant 的功能基础上又新增了依赖管理功能,而 Gradle 可以使用 Maven 的仓库,改用代码编写构建脚本。

-Gradle 和 Maven 的区别是,它使用 Groovy 这种特定领域语言(DSL)来管理构建脚本,而不再使用 XML 这种标记性语言。因为项目如果庞大的话,XML 很容易就变得臃肿。 +Gradle 和 Maven 的区别是,Gradle 的构建脚本用基于 Groovy 或 Kotlin 的特定领域语言(DSL)编写,而不再使用 XML 这种标记性语言。因为项目如果庞大的话,XML 很容易就变得臃肿。 例如要在项目中引入 JUnit,Maven 的代码如下: @@ -47,33 +48,45 @@ Gradle 和 Maven 的区别是,它使用 Groovy 这种特定领域语言(DSL ``` -而 Gradle 只需要几行代码: +而 Gradle 的完整 build.gradle 只需要几行代码: ```groovy +plugins { + id "java" +} + +repositories { + mavenCentral() +} + dependencies { testImplementation "junit:junit:4.10" } ``` +和 Maven 不同,Gradle 不默认使用中央仓库,也要先应用 java 插件才有 testImplementation 这类依赖配置,所以这两段不能省略(Gradle 9.8 实测,见[附录 A1](#a1-gradle-构建脚本的最小写法))。 + 早期版本中的 testCompile 在 Gradle 7 中已被移除,改用 testImplementation。Gradle 也可以用 Kotlin 编写构建脚本(build.gradle.kts),Gradle 8.2 起新建的项目默认使用 Kotlin。 +两者处理依赖冲突的规则也不同:Maven 选路径最短的版本,见下文的[依赖原则](#依赖原则);Gradle 默认在所有请求的版本中选最高的一个。 + ## 三、Maven ### 概述 -提供了项目对象模型(POM)文件来管理项目的构建。 +Maven 用项目对象模型(Project Object Model, POM)文件描述项目和它的依赖,并约定了目录结构和构建的生命周期:源码放在 src/main/java,执行 mvn package 时依次完成编译、测试和打包,在 target 目录生成 jar 包。 ### 仓库 -仓库的搜索顺序为:本地仓库、远程仓库、中央仓库。 +Maven 先在本地仓库中查找依赖,找不到时再按配置的顺序访问远程仓库下载,中央仓库排在 POM 中声明的仓库之后。 -- 本地仓库用来存储项目的依赖库; -- 中央仓库是下载依赖库的默认位置; -- 远程仓库,因为并非所有的依赖库都在中央仓库,或者中央仓库访问速度很慢,远程仓库是中央仓库的补充。 +- 本地仓库是本机上的目录(默认 ~/.m2/repository),缓存下载过的依赖,找到了就不再联网,SNAPSHOT 版本除外,它会按更新策略检查远程仓库有没有新版本; +- 中央仓库(Maven Central)是 Maven 默认使用的远程仓库; +- 其它远程仓库是中央仓库的补充,例如公司的私服,用来存放中央仓库没有的依赖,或者在中央仓库访问很慢时提供镜像。 ### POM -POM 代表项目对象模型,它是一个 XML 文件,保存在项目根目录的 pom.xml 文件中。 +POM 是一个 XML 文件,保存在项目根目录的 pom.xml 中。例如下面声明了测试时使用的 JUnit: ```xml @@ -84,9 +97,9 @@ POM 代表项目对象模型,它是一个 XML 文件,保存在项目根目 ``` -[groupId, artifactId, version, packaging, classifier] 称为一个项目的坐标,其中 groupId、artifactId、version 必须定义,packaging 可选(默认为 Jar),classifier 不能直接定义的,需要结合插件使用。 +[groupId, artifactId, version, packaging, classifier] 称为一个项目的坐标,其中 groupId、artifactId、version 必须定义,packaging 可选(默认为 jar),classifier 不能直接定义,需要结合插件使用。 -- groupId:项目组 Id,必须全球唯一; +- groupId:组织或项目组的标识,通常是反写的域名,例如 org.apache.maven; - artifactId:项目 Id,即项目名; - version:项目版本; - packaging:项目打包方式。 @@ -116,12 +129,43 @@ A -> C -> X(2.0) ### 解决依赖冲突 -先用 `mvn dependency:tree` 查看依赖树,找到冲突的版本分别是从哪条路径引入的,再用以下方法确定使用的版本: +先用 `mvn dependency:tree -Dverbose` 查看依赖树,找到冲突的版本分别是从哪条路径引入的。例如“依赖路径最短优先原则”中的例子,被舍弃的版本会标为 omitted for conflict: + +```text +demo:a:pom:1.0 ++- demo:b:jar:1.0:compile +| \- demo:c:jar:1.0:compile +| \- (demo:x:jar:1.0:compile - omitted for conflict with 2.0) +\- demo:d:jar:1.0:compile + \- demo:x:jar:2.0:compile +``` + +再用以下方法确定使用的版本: - 在 dependencyManagement 中声明版本,所有传递依赖都使用这个版本; - 用 exclusions 排除某个依赖传递过来的版本; - 在当前 POM 中直接声明需要的版本,它的路径最短。 +## 附录:实测 + +### A1. Gradle 构建脚本的最小写法 + +环境:Gradle 9.8.2,JDK 21.0.13,macOS。 + +只写 dependencies 一段时,脚本求值失败: + +```text +> Could not find method testImplementation() for arguments [junit:junit:4.10] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler. +``` + +加上 plugins 和 repositories 后,`gradle -q dependencies --configuration testRuntimeClasspath` 的输出: + +```text +testRuntimeClasspath - Runtime classpath of source set 'test'. +\--- junit:junit:4.10 + \--- org.hamcrest:hamcrest-core:1.1 +``` + ## 参考资料 - [POM Reference](http://maven.apache.org/pom.html#Dependency_Version_Requirement_Specification)