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 | - |
下載連結: https://download.oracle.com/java/17/latest/jdk-17_windows-x64_bin.exe
下載後靜默安裝即可,按需修改 JDK 路徑(D:\Java\jdk-17.0.3.1)
設定環境 JDK 環境變數非必須!考慮到大多數人因為老專案JAVA_HOME設定JDK8的情況,下文是通過設定 Gradle 指定 JDK 版本方式。
新增系統變數 JAVA_HOME = D:\Java\jdk-17.0.3.1
新增Path:%JAVA_HOME%\bin
驗證:java -version
下載地址:https://gradle.org/releases
下載解壓到指定目錄(D:\softs\gradle-7.6)
新增系統變數:GRADLE_HOME=D:\softs\gradle-7.6
新增至Path路徑(%GRADLE_HOME%\bin)
檢視版本 gradle -v
在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()
}
}
}
不建議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
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
在完成上述的原始碼匯入和相關設定之後,就可以進行原始碼編譯了。
參考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 模組做個簡單的測試。
File → Module 新增 spring-sample
範例模組
在 spring-sample
模組下的 build.gradle
新增 spring-context
依賴,它是包含了 spring-core
、 spring-bean
和 IoC容器等Spring 執行時上下文的依賴。
api(project(":spring-context"))
程式碼結構
/**
* 人介面
*/
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();
}
}
image-20221210232239371
本章節就基於編譯好的 Spring 原始碼環境進行原始碼偵錯,為了方便下面就基於上章節的測試案例來對 getBean
原始碼流程分析,後續會更新出 Spring 原始碼閱讀系列文章。
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定義去建立新範例。
後續更新 Spring 6
原始碼閱讀系列 @有來技術。
在編譯過程中,因環境不同每個人可能遇到的問題也都不同,但是總結出來的都是沒按照官方檔案要求或者自己粗心所致,下面就總結編譯中遇到常見的問題,也希望大家在留言區把自己遇到問題記錄下。
報錯詳情
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
歡迎大家留言區補充或提問~
本篇從 Spring 6 編譯依賴的基礎環境搭建(JDK17和Gradle)開始、根據官方檔案編譯原始碼、在工程新增範例模組測試、以及最後通過對getBean的原始碼偵錯,繪製時序圖和類註釋輔助手段來掌握高效閱讀Spring原始碼技巧。還有一點需要提醒,一定要帶著一個明確的目的去看原始碼,不要被動式的為了學習而學習,不然很容易在知識的海洋裡嗆水。最後預祝大家編譯成功,掌握到屬於自己高效閱讀原始碼的方式。
持續更新~
Spring 6 編譯原始碼倉庫地址: https://gitee.com/youlaiorg/spring-framework