Spring 6 源码编译和高效阅读源码技巧分享_java

Spring Boot 3 RELEASE版本于2022年11月24日正式发布,其核心框架的 Spring 也在2022年11月16日迎来了从 5.3.x到6.0.x重大版本升级,借着这个机会,更新下 Spring 6 源码编译以及分享下如何高效阅读源码的技巧。

一. 前言

​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)

Spring 6 源码编译和高效阅读源码技巧分享_Spring_02

2. 配置环境变量(可忽略)

配置环境 JDK 环境变量非必须!考虑到大多数人因为老项目JAVA_HOME配置JDK8的情况,下文是通过设置 Gradle 指定 JDK 版本方式。

添加系统变量 JAVA_HOME = D:\Java\jdk-17.0.3.1

Spring 6 源码编译和高效阅读源码技巧分享_maven_03

添加Path:​​%JAVA_HOME%\bin​

Spring 6 源码编译和高效阅读源码技巧分享_maven_04

验证:​​java -version​

Spring 6 源码编译和高效阅读源码技巧分享_spring_05

四. Gradle 安装

1. 下载Gradle

下载地址:​​https://gradle.org/releases​

下载解压到指定目录(D:\softs\gradle-7.6)

Spring 6 源码编译和高效阅读源码技巧分享_spring_06

2. 配置环境变量

添加系统变量:GRADLE_HOME=D:\softs\gradle-7.6

Spring 6 源码编译和高效阅读源码技巧分享_Spring_07

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

Spring 6 源码编译和高效阅读源码技巧分享_maven_08

查看版本 gradle -v

Spring 6 源码编译和高效阅读源码技巧分享_spring_09

3. 配置镜像仓库

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

Spring 6 源码编译和高效阅读源码技巧分享_Spring_10

参考阿里云官方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​

Spring 6 源码编译和高效阅读源码技巧分享_Spring_11

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​

国内镜像,速度极快

Spring 6 源码编译和高效阅读源码技巧分享_Spring_12

Spring 6 源码编译和高效阅读源码技巧分享_java_13

IDEA源码获取完成之后,因为当前时间最新稳定版tag是v6.0.2版本 ,所以还需要进行分支切换:

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

Spring 6 源码编译和高效阅读源码技巧分享_java_14

Spring 6 源码编译和高效阅读源码技巧分享_Spring_15

2. 环境设置

  • IDEA设置
    File → Settings → Build,Execution,Deployment → Build Tools → Gradle
  • build.gradle
    找到 repositories 配置节点,新增阿里云镜像仓库地址
maven { url "https://maven.aliyun.com/repository/public" } // 阿里云镜像仓库

Spring 6 源码编译和高效阅读源码技巧分享_Spring_16

  • settings.gradle
    找到 repositories 配置节点,新增阿里云镜像仓库地址
maven { url "https://maven.aliyun.com/repository/public" } // 阿里云镜像仓库

Spring 6 源码编译和高效阅读源码技巧分享_java_17

  • gradle.properties
    项目内 ​​gradle.properties​​ 配置文件添加java路径
org.gradle.java.home=D:\Java\jdk-17.0.3.1

3. 编译步骤

在完成上述的源码导入和相关设置之后,就可以进行源码编译了。

参考IDEA导入说明文档 ​​import-into-idea.md​​ ,仅需三步:

Spring 6 源码编译和高效阅读源码技巧分享_java_18

  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​​ 示例模块

Spring 6 源码编译和高效阅读源码技巧分享_maven_19

2. 添加依赖

在 ​​spring-sample​​​ 模块下的 ​​build.gradle​​​ 新增 ​​spring-context​​​ 依赖,它是包含了 ​​spring-core​​​、 ​​spring-bean​​ 和 IoC容器等Spring 运行时上下文的依赖。

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

Spring 6 源码编译和高效阅读源码技巧分享_spring_20

3. 测试代码

代码结构

Spring 6 源码编译和高效阅读源码技巧分享_Spring_21

/**
* 人接口
*/
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. 测试结果

Spring 6 源码编译和高效阅读源码技巧分享_spring_22

image-20221210232239371

七. 源码阅读

本章节就基于编译好的 Spring 源码环境进行源码调试,为了方便下面就基于上章节的测试案例来对 ​​getBean​​ 源码流程分析,后续会更新出 Spring 源码阅读系列文章。

1. getBean 源码

  • 快速定位: 通过 Debug (​​F7​​)可以很清晰看到详细的调用栈

Spring 6 源码编译和高效阅读源码技巧分享_maven_23

image-20221210230131704

Spring 6 源码编译和高效阅读源码技巧分享_Spring_24

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​