Spring 6 源碼編譯和高效閱讀源碼技巧分享

一. 前言

Spring Boot 3 RELEASE版本於 2022年11月24日 正式發佈,相信已經有不少同學開始準備新版本的學習了,不過目前還不建議在實際項目中做升級,畢竟還有很多框架和中間件沒出適配版本。此次Spring Boot里程碑的升級也要求了最低JDK 17Spring Framework 6 ,其核心框架的 Spring 也在 2022年11月16日 迎來了從 5.3.x6.0.x 重大版本升級,藉着這個機會,寫一篇關於 Spring 6 源碼編譯和如何高效閱讀 Spring 源碼的教程。

二. 環境聲明

Spring源碼編譯官方文檔:https://github.com/spring-projects/spring-framework/wiki/Build-from-Source

根據官方文檔描述, Spring 6 需要 JDK 17

基礎環境 版本 本地路徑
操作系統 Windows 11 -
Spring源碼 6.0.2 D:\SourceCode\spring-framework
Java環境 JDK 17 D:\Java\jdk-17.0.3.1
編譯工具 Gradle 7.6 D:\softs\gradle-7.6
開發工具 IDEA 2022.2.3 -

三. JDK 安裝

1. 下載JDK17

下載鏈接: https://download.oracle.com/java/17/latest/jdk-17_windows-x64_bin.exe

下載後靜默安裝即可,按需修改 JDK 路徑(D:\Java\jdk-17.0.3.1)

2. 配置環境變量(可忽略)

配置環境 JDK 環境變量非必須!考慮到大多數人因爲老項目JAVA_HOME配置JDK8的情況,下文是通過設置 Gradle 指定 JDK 版本方式。

添加系統變量 JAVA_HOME = D:\Java\jdk-17.0.3.1

添加Path:%JAVA_HOME%\bin

驗證:java -version

四. Gradle 安裝

1. 下載Gradle

下載地址:https://gradle.org/releases

下載解壓到指定目錄(D:\softs\gradle-7.6)

2. 配置環境變量

添加系統變量:GRADLE_HOME=D:\softs\gradle-7.6

添加至Path路徑(%GRADLE_HOME%\bin)

查看版本 gradle -v

3. 配置鏡像倉庫

在gradle安裝位置(D:\softs\gradle-7.6\init.d) 目錄下新建 init.gradle 文件

參考阿里雲官方gradle配置指南:https://developer.aliyun.com/mvn/guide ,init.gradle 完整內容如下

allprojects {
    repositories {
        maven { url 'file:///D:/data/.m2/repository'} // 本地倉庫地址,如果沒有依次向下尋找
        maven { url "https://maven.aliyun.com/repository/public" }
        mavenLocal()
        mavenCentral()
    }
    buildscript {
        repositories {
            maven { url 'https://maven.aliyun.com/repository/public' }
            mavenLocal()
	    mavenCentral()
        }
    }
}

五. 源碼編譯

1. 獲取Spring源碼

不建議zip包方式下載源代碼,具體看官方issue:https://github.com/spring-projects/spring-framework/issues/24467

IDEA 選擇 File → New → Project from Version Control 輸入Spring源碼倉庫地址:

地址 備註
Github https://github.com/spring-projects/spring-framework.git 速度慢
GitCode https://gitcode.net/mirrors/spring-projects/spring-framework.git 國內鏡像,速度極快

IDEA源碼獲取完成之後,因爲當前時間最新穩定版tag是v6.0.2版本 ,所以還需要進行分支切換:

git checkout -b v6.0.2
git pull origin v6.0.2

2. 環境設置

  • IDEA設置

    File → Settings → Build,Execution,Deployment → Build Tools → Gradle

  • build.gradle

    找到 repositories 配置節點,新增阿里雲鏡像倉庫地址

    maven { url "https://maven.aliyun.com/repository/public" } // 阿里雲鏡像倉庫
    

  • settings.gradle

    找到 repositories 配置節點,新增阿里雲鏡像倉庫地址

    maven { url "https://maven.aliyun.com/repository/public" } // 阿里雲鏡像倉庫
    

  • gradle.properties
    項目內 gradle.properties 配置文件添加java路徑

    org.gradle.java.home=D:\Java\jdk-17.0.3.1
    

3. 編譯步驟

在完成上述的源碼導入和相關設置之後,就可以進行源碼編譯了。

參考IDEA導入說明文檔 import-into-idea.md ,僅需三步:

  1. Precompile spring-oxm with ./gradlew :spring-oxm:compileTestJava

    Windows 環境 CMD 輸入 gradlew :spring-oxm:compileTestJava 先執行 spring-oxm 的預編譯

  2. Import into IntelliJ (File -> New -> Project from Existing Sources -> Navigate to directory -> Select build.gradle)

    File → New → Project from Existing Sources → Select File or Directory to import 選擇 build.gradle 點擊 OK 完成編譯

  3. When prompted exclude the spring-aspects module (or after the import via File-> Project Structure -> Modules)

六. 測試案例

在完成上文 Spring 源碼編譯之後,Congratulations ! 接下來新增一個示例模塊來依賴工程中的其它 spring 模塊做個簡單的測試。

1. 新增模塊

File → Module 新增 spring-sample 示例模塊

2. 添加依賴

spring-sample 模塊下的 build.gradle 新增 spring-context 依賴,它是包含了 spring-corespring-bean 和 IoC容器等Spring 運行時上下文的依賴。

 api(project(":spring-context"))

3. 測試代碼

代碼結構

/**
 * 人接口
 */
public interface IPersonService {

 /**
  * 說
  */
 void speak();

}
/**
 * 中國人
 */
@Service
@Primary
public class ChineseService implements IPersonService {
 @Override
 public void speak() {
  System.out.println("我會說中文");
 }
}
/**
 * 美國人
 */
@Service
public class AmericanService implements IPersonService {
 @Override
 public void speak() {
  System.out.println("I can speak English");
 }
}
/**
 * 啓動測試類
 */
@ComponentScan("com.youlai.spring.sample.**")
public class SpringSampleApplication {

 public static void main(String[] args) {
  AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
    SpringSampleApplication.class
  );

  IPersonService personService = context.getBean(IPersonService.class);
  personService.speak();
 }
}

4. 測試結果

image-20221210232239371

image-20221210232239371

七. 源碼閱讀

本章節就基於編譯好的 Spring 源碼環境進行源碼調試,爲了方便下面就基於上章節的測試案例來對 getBean 源碼流程分析,後續會更新出 Spring 源碼閱讀系列文章。

1. getBean 源碼

  • 快速定位: 通過 Debug (F7)可以很清晰看到詳細的調用棧

image-20221210230131704

image-20221210230131704

getBean時序圖

getBean時序圖

  • 深入概念原理

    時序圖反映了在getBean()調用鏈中 DefaultListableBeanFactory 承擔着核心角色,甚至可以說是 Spring 最核心的一個 BeanFacory 實現 ,也被稱爲 Spring 的 “發動機”,所以其重要性是學習 Spring 源碼的必修課。

    DefaultListableBeanFactory : 可枚舉的Bean工廠。

​ 通過類註釋我們可以瞭解到:DefaultListableBeanFactory 是一個成熟的bean工廠;包含了 bean 定義元數據(beanDefinitionMap),提供了Bean定義的註冊和獲取方法;管理已存在的Bean實例,而不是基於Bean定義去創建新實例。

2. todo

​ 後續更新 Spring 6 源碼閱讀系列 @有來技術。

八. 問題整理

在編譯過程中,因環境不同每個人可能遇到的問題也都不同,但是總結出來的都是沒按照官方文檔要求或者自己粗心所致,下面就總結編譯中遇到常見的問題,也希望大家在留言區把自己遇到問題記錄下。

1. 問題一

  • 報錯詳情

    D:\SourceCode\spring-framework>gradlew :spring-oxm:compileTestJava
    
    > Task :buildSrc:compileJava FAILED
    D:\SourceCode\spring-framework\buildSrc\src\main\java\org\springframework\build\KotlinConventions.java:44: 錯誤: 找不到符號
                    freeCompilerArgs.addAll(List.of("-Xsuppress-version-warnings", "-Xjsr305=strict", "-opt-in=kotlin.RequiresOptIn"));
                                                ^
      符號:   方法 of(java.lang.String,java.lang.String,java.lang.String)
      位置: 接口 java.util.List
    1 個錯誤
    
    FAILURE: Build failed with an exception.
    
  • 解決方案

    gradlew :spring-oxm:compileTestJava info 查看使用 JDK 的版本是不是17,如果不是請在配置文件 gradle.properties 添加:

    org.gradle.java.home=D:\Java\jdk-17.0.3.1
    

2. todo

​ 歡迎大家留言區補充或提問~

九. 結語

本篇從 Spring 6 編譯依賴的基礎環境搭建(JDK17和Gradle)開始、根據官方文檔編譯源碼、在工程新增示例模塊測試、以及最後通過對getBean的源碼調試,繪製時序圖和類註釋輔助手段來掌握高效閱讀Spring源碼技巧。還有一點需要提醒,一定要帶着一個明確的目的去看源碼,不要被動式的爲了學習而學習,不然很容易在知識的海洋裏嗆水。最後預祝大家編譯成功,掌握到屬於自己高效閱讀源碼的方式。

持續更新~

附. 源碼

Spring 6 編譯源碼倉庫地址: https://gitee.com/youlaiorg/spring-framework

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章