举手示意一下,我们中有多少人真正理解构建自动化工具是如何构建其依赖树的?现在,如果你是因为从事构建自动化工具的工作而理解这一点,请放下你的手。我猜就是这样!
软件工程师一个令人头疼的责任就是理解项目的依赖关系:哪些传递依赖被引入以及由谁引入;为什么使用v1.3.1而不是声明的v1.2.10;当传递依赖发生变化时产生了什么结果;同一构件的多个版本是如何出现的?
每位软件工程师都曾将依赖树输出到文本文件中,搜索特定的构件,然后追溯其来源。对于任何非微不足道的项目,要在脑海中构建出依赖关系的图谱极其困难,甚至是不可能的。
I faced this problem when starting a new job with a mature code base, presenting a challenge to assemble the puzzle pieces. I’ve previously worked with graph databases and thought a graphical view of the dependency artifacts could be created using Neo4J, which resulted in DependencyLoader.
注意:这不是关于图数据库的教程,也不要求有图数据库的背景知识。如果感兴趣,Neo4J提供了教程和白皮书来帮助你入门。
设置环境
安装Java
需要Java 11或更高版本。如尚未安装,请安装您偏好的OpenJDK版本。
安装Neo4J
本教程需使用一个Neo4J数据库,用于加载依赖信息,最好是不共享的,因为每次运行前加载器会清空数据库。特此提醒!
Neo4J 提供个人沙箱环境,非常适合此类短期教程项目。
或者,您也可以在您的台式机或笔记本上本地安装Neo4J。Homebrew简化了MacOS上的安装过程:
brew install neo4j && brew services start neo4j
继续之前,请通过浏览器确认您能访问Neo4J数据库,可以使用Neo4J沙箱的链接和凭证,或本地访问http://localhost:7474。本地安装的默认凭证是neo4j/neo4j;成功登录后,系统会强制您更改密码。
克隆仓库
neo4j-gradle-dependencies仓库包含用于将依赖项加载到Neo4J的工具。本教程将生成spring-boot的依赖关系图。您必须克隆这两个仓库。
Scott.Sosna@mymachine src% git clone [email protected]:scsosna99/neo4j-gradle-dependencies.git
Scott.Sosna@mymachine src% git clone [email protected]:spring-projects/spring-boot.git
注意:本地无需安装Gradle,因为两个仓库都使用了Gradle Wrapper,该Wrapper首次使用时会自动下载所有必需组件。
生成依赖项
DependencyLoader
以Gradle生成的依赖树作为输入。虽然可以同时加载多个配置——例如compileClasspath
、runtimeClasspath
、testCompileClasspath
、testRuntimeClasspath
——但针对教程而言,从一个配置开始更容易理解。
要为所有配置生成依赖项:
-
gradle dependencies
-
./gradlew dependencies
要为单个配置生成依赖项:
-
gradle dependencies --configuration <configuration>
-
./gradlew dependencies --configuration <configuration>
生成Spring Boot依赖项
本教程使用Spring Boot的compileClasspath
依赖项在Neo4J中创建一个依赖关系图。从克隆仓库的目录中,执行以下命令:
Scott.Sosna@mymachine src% cd spring-boot/spring-boot-project/spring-boot
Scott.Sosna@mymachine spring-boot% ./gradlew dependencies --configuration compileClasspath > dependencies.out
文件dependencies.out
包含了Spring Boot的编译时类路径依赖项。
加载依赖项
首先,确认DependencyLoader.java中的连接URL和认证凭据,并在必要时进行修改。
执行以下命令将Spring Boot的依赖项加载到Neo4j中:
Scott.Sosna@mymachine spring-boot% cd ../../../neo4j-gradle-dependencies
Scott.Sosna@mymachine neo4j-gradle-dependencies% ./gradlew clean run --args="../spring-boot/spring-boot-project/spring-boot/dependencies.out"
成功后,gradle的输出行将显示:
Scott.Sosna@PVHY32M6KG neo4j-gradle-dependencies % ./gradlew clean run --args="../spring-boot/spring-boot-project/spring-boot/dependencies.out"
> Task :compileJava
Note: /Users/Scott.Sosna/data/src/github/neo4j-gradle-dependencies/src/main/java/dev/scottsosna/neo4j/gradle/relationship/DependsOn.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
> Task :run
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Jun 02, 2023 6:19:22 AM org.neo4j.driver.internal.logging.JULogger info
INFO: Direct driver instance 1606286799 created for server address localhost:7687
dependencies.out completed.
Jun 02, 2023 6:19:23 AM org.neo4j.driver.internal.logging.JULogger info
INFO: Closing driver instance 1606286799
Jun 02, 2023 6:19:23 AM org.neo4j.driver.internal.logging.JULogger info
INFO: Closing connection pool towards localhost:7687
Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
See https://docs.gradle.org/7.5.1/userguide/command_line_interface.html#sec:command_line_warnings
BUILD SUCCESSFUL in 3s
查看依赖项
有多种工具可用于显示Neo4J图,但本教程内置的浏览器工具已足够。
显示完整树
查询MATCH(a) RETURN a
相当于关系型数据库中的SELECT * FROM <table>
查看构件详情
找到的每个构件都会创建一个节点,其属性标识构件(groupId/artifactId)及其类型,显示在右侧窗格中。
查看依赖详情
每个依赖都以关系的形式创建,其属性标识了依赖的具体信息:配置、指定/版本及配置。下方选定的依赖显示spring-security:spring-web依赖于io.micormeter:micrometer-observation,但spring-web指定的版本1.10.7被解析为版本1.11.0。
遍历依赖关系
Neo4J允许您逐个节点地探索图,让您能够手动逐个扩展图节点,提供了一种探索依赖树特定区域的方式。
假设您想要了解io.projectreactor.netty:reactor-netty-http这个构件的依赖关系。首先,我们将针对该特定节点向Neo4J发起查询。
MATCH(a:Artifact {groupId: 'io.projectreactor.netty', artifactId: 'reactor-netty-http'}) RETURN a
双击该节点将展示其邻近节点——依赖于它的构件以及它所依赖的构件。
这个扩展的图显示了一个依赖于它的构件——项目的根,其构件类型为PROJECT,以及它所依赖的其他六个构件。
接下来,双击io.netty:netty-codehttps://github.com/netty/netty/tree/4.1/codec-httpc-http以展示下一级别的依赖关系。请注意,除了所选节点的关系(依赖)外,图上已存在的节点可能还会显示额外的关系。
识别版本不匹配
Gradle的依赖输出指示了指定版本与Gradle解析的版本不一致的情况。依赖(关系)的属性可用于Neo4J查询,限制显示的关系及附加的构件(节点)。
MATCH (a:Artifact)-[d:DEPENDS_ON]->(b:Artifact) WHERE d.specifiedVersion<>d.resolvedVersion RETURN a,b,d
如有需要,Neo4J可以以表格形式返回结果,便于审阅。
MATCH (a:Artifact)-[d:DEPENDS_ON]->(b:Artifact) WHERE d.specifiedVersion<>d.resolvedVersion RETURNa.name AS source, b.name AS dependency, d.specifiedVersion AS specified, d.resolvedVersion AS resolved
附加信息
mappings.out
mappings.out文件允许你根据构件的groupId自定义分配给节点的构件类型,最常见的是为了专门识别你组织创建的构件。
输入目录
DependencyLoader
的命令行参数可能是一个包含多个Gradle依赖树的目录,这些依赖树被加载到同一个Neo4J数据库中。这有助于理解具有独立build.gradle文件的相关项目的依赖关系。
限制与省略
Gradle将某些依赖标识为受限制的和省略的。目前,这些依赖并未被加载,但很容易通过为关系创建额外的属性来包含。
Source:
https://dzone.com/articles/understanding-dependenciesvisually