一. 前言
Spring Boot 3
RELEASE版本於 2022年11月24日 正式釋出,相信已經有不少同學開始準備新版本的學習了,不過目前還不建議在實際專案中做升級,畢竟還有很多框架和中介軟體沒出適配版本。此次Spring Boot
里程碑的升級也要求了最低JDK 17
和 Spring Framework 6
,其核心框架的 Spring 也在 2022年11月16日 迎來了從 5.3.x
到 6.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
,僅需三步:
-
Precompile
spring-oxm
with./gradlew :spring-oxm:compileTestJava
Windows 環境 CMD 輸入
gradlew :spring-oxm:compileTestJava
先執行spring-oxm
的預編譯 -
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 完成編譯 -
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-core
、 spring-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
七. 原始碼閱讀
本章節就基於編譯好的 Spring 原始碼環境進行原始碼除錯,為了方便下面就基於上章節的測試案例來對 getBean
原始碼流程分析,後續會更新出 Spring 原始碼閱讀系列文章。
1. getBean 原始碼
- 快速定位: 透過 Debug (
F7
)可以很清晰看到詳細的呼叫棧
image-20221210230131704
-
加深理解記憶: 基於呼叫棧繪製時序圖(IDEA的
PlantUML
外掛)時序圖原始檔:https://gitee.com/youlaiorg/spring-framework/blob/master/doc/diagram/getBean.puml
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