視覺化理解依賴關係!

舉手示意,我們中有多少人真正理解您的建構自動化工具是如何建立其依賴樹的?現在,如果您是因為從事建構自動化工具的工作而理解,請放下手。我想就是這樣!

軟體工程師一個令人沮喪的職責是理解專案的依賴關係:哪些傳遞性依賴被引入以及是由誰引入的;為什麼使用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的安装过程:

Shell

 

brew install neo4j && brew services start neo4j

继续之前,请通过浏览器确认您对Neo4J数据库的访问,可以使用Neo4J沙箱的链接和凭据,或者在本地访问http://localhost:7474。本地安装的默认凭据是neo4j/neo4j;登录成功后,您将被强制更改密码。

克隆存储库

neo4j-gradle-dependencies 儲存庫包含了用於將依賴項載入到 Neo4J 的工具。本教程將為 spring-boot 生成一個依賴圖。您必須克隆這兩個儲存庫。

Shell

 

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,該包裝器在首次使用時會下載所有必要的組件。

生成依賴項

DependencyLoaderGradle 生成的依賴樹 作為輸入。雖然可以同時載入多個配置,例如 compileClasspathruntimeClasspathtestCompileClasspathtestRuntimeClasspath,但從單一配置開始更容易導航,尤其是在教程中。

要為所有配置生成依賴項:

  • gradle dependencies
  • ./gradlew dependencies

要為單一配置生成依賴項

  • gradle dependencies --configuration <configuration>
  • ./gradlew dependencies --configuration <configuration>

生成 Spring Boot 依賴項

本教學將在Neo4J中建立一個依賴圖,使用Spring Boot的compileClasspath依賴。從存放庫被複製的目錄中,執行以下命令:

Shell

 

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中:

Shell

 

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的輸出信息將如下:

Shell

 

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>

查看Artifact詳情

每個找到的Artifact都會創建一個節點,其屬性識別Artifact(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查詢該特定節點。

Cypher

 

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 查詢,限制顯示的關係及其附加的工件(節點)。

Cypher

 

MATCH (a:Artifact)-[d:DEPENDS_ON]->(b:Artifact) WHERE d.specifiedVersion<>d.resolvedVersion RETURN a,b,d

如有需要,Neo4J 可以以表格格式返回結果,以便於審查。

Cypher

 

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