spring源码阅读
源码环境版本
-
spring 源码版本选择的 5.3.22 的分支
-
gradle选择7.2版本,在编译 spring 源码可以不用安装 gradle,使用源码中自带的 grade wrepper 中的命令就可以了,但后面自己创建测试项目时还是要安装 gradle,所以版本选择了和 grade wrepper 中相同的版本 gradle7.2。安装很简单,官网下载对应版本并解压,配置环境变量
GRADLE_HOME
、Path 变量添加%GRADLE_HOME%\bin
并配置了GRADLE_USER_HOME
选择一个文件夹作为 gradle 下载依赖时的本地缓存仓库地址。说明:gradle wrapper 可以使得当前环境中不用安装 gradle ,直接使用 gradlew 命令来运行即可。spring 项目中自带的 gradle wrapper 版本可以在项目根目录
gradle/wrapper
目录下的gradle-wrapper.properties
文件中查看,这里 spring5.3.x 中定义如下:1
2
3
4
5distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists -
jdk选择jdk11,因为 spring 源码(
org.springframework.core.metrics.jfr.FlightRecorderStartupStep
)中用到了 jdk9 之后的新特性 JFR,因为之前一直用的 jdk8, 后面编译的时候会报 jfr 符号找不到的错误,错误提示如下:1
2
3
4
5...
core\src\main\java\org\springframework\core\metrics\jfr\FlightRecorderStartupEvent.java:19: 错误: 程序包jdk.jfr不存在
import jdk.jfr.Category;
^
...所以这里提前再安装一个长期支持版本 jdk11,本机上之前用的是 jdk8
-
idea2022.1.3,idea 版本较低会导致后面运行出错,之前一直用的 idea2019.2.2 导致项目一直导入不了,idea 中对 gradle 默认支持的版本可以从 idea 安装目录
plugins/gradle/lib
下查看。 -
这里有两种方式可以下载
5.3.22
版本的代码 - 压缩包方式:在网页上切换到相应分支后下载压缩文件
-
git命令方式:使用命令
git clone -b 5.3.22 https://github.com/spring-projects/spring-framework.git
下载源码,或者使用命令克隆主分支后再使用git checkout 5.3.22
切换分支 -
根据 官网说明 ,需要先编译
spring-oxm
再导入到 idea 中,因此在项目根目录打开cmd
窗口,执行命令gradlew.bat :spring-oxm:compileTestJava
这里因为在前面安装了相同版本的 gradle ,因此使用 gradle 执行命令也是一样的。在执行命令之前,建议先在项目配置文件中添加阿里云仓库地址,加快依赖下载的速度,具体如下: -
项目根目录的
build.gralde
文件中的configure>dependencyManagement>repositories
节点添加仓库地址:1
2
3
4repositories {
maven { url 'https://maven.aliyun.com/repository/public/' }
。。。
} -
项目根目录的
settings.gralde
文件中的pluginManagement>repositories
节点添加仓库地址:1
2
3
4repositories {
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
。。。
} -
以及根目录中
buildSrc
目录下build.gralde
文件中的repositories
节点添加仓库地址:1
2
3
4repositories {
maven { url 'https://maven.aliyun.com/repository/public/' }
。。。
}首次运行报错如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27Starting a Gradle Daemon (subsequent builds will be faster)
Task :spring-core:compileJava
E:\self\work\code\spring\spring-04\spring-framework\spring-core\src\main\java\org\springframework\core\CoroutinesUtils.java:74: 警告: [deprecation] AccessibleObject中的isAccessible()已过时
if (method.isAccessible() && !KCallablesJvm.isAccessible(function)) {
^
错误: 发现警告, 但指定了 -Werror
1 个错误
1 个警告
Task :spring-core:compileJava FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':spring-core:compileJava'.
Compilation failed; see the compiler error output for details.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 1m 16s
20 actionable tasks: 17 executed, 3 from cache
A build scan was not published as you have not authenticated with server 'ge.spring.io'.以上结果中有三个错误:
-
AccessibleObject中的isAccessible()已过时:这个类是 jdk 源码中反射包下的类,查看源码得知这个方法从 jdk9 已被废弃,并提供了替代方法
canAccess(Object)
。因此这里可以将method.isAccessible()
方法替换为method.canAccess(Object)
方法,也可以在CoroutinesUtils.invokeSuspendingFunction()
上加上注解@SuppressWarnings("deprecation")
使之忽略掉警告,它位于spring-core
模块下,全类名org.springframework.core.CoroutinesUtils
。这里选择添加忽略警告的注解。 -
错误: 发现警告, 但指定了 -Werror:这个是 kotlin 的编译参数,搜了一下网上都是在 idea 中修改,但是后面发现每次修改了之后,在重新打开 idea 还是会报这个错误,后面通过查看
build.gradle
文件,发现其中有一个 kotlin 编译参数allWarningsAsErrors = true
,将其注释掉即可。 -
没有访问
ge.spring.io
:这里需要修改setting.gradle
文件将plugins
节点中的id "io.spring.ge.conventions" version "0.0.10"
注释掉即可 -
重新运行打开
cmd
窗口,运行命令gradlew.bat :spring-oxm:compileTestJava
,第二次运行结果如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14Task :spring-beans:compileGroovy
注: E:\self\work\code\spring\spring-04\spring-framework\spring-beans\build\tmp\compileGroovy\groovy-java-stubs\org\springframework\beans\factory\groovy\GroovyDynamicElementReader.java使用或覆盖了已过时的 API。
注: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。
Task :spring-oxm:xjcGenerate
Errors occurred while build effective model from F:\gradle_repo\caches\modules-2\files-2.1\com.sun.xml.bind\jaxb-xjc\2.2.11\4da3d39ae8ccc39549e3f8971f56035f61d7bf79\jaxb-xjc-2.2.11.pom:
'dependencyManagement.dependencies.dependency.systemPath' for com.sun:tools:jar must specify an absolute path but is ${tools.jar} in com.sun.xml.bind:jaxb-xjc:2.2.11
Errors occurred while build effective model from F:\gradle_repo\caches\modules-2\files-2.1\com.sun.xml.bind\jaxb-core\2.2.11\db0f76866c6b1e50084e03ee8cf9ce6b19becdb3\jaxb-core-2.2.11.pom:
'dependencyManagement.dependencies.dependency.systemPath' for com.sun:tools:jar must specify an absolute path but is ${tools.jar} in com.sun.xml.bind:jaxb-core:2.2.11
Errors occurred while build effective model from F:\gradle_repo\caches\modules-2\files-2.1\com.sun.xml.bind\jaxb-impl\2.2.11\2d4b554997fd01d1a2233b1529b22fc9ecc0cf5c\jaxb-impl-2.2.11.pom:
'dependencyManagement.dependencies.dependency.systemPath' for com.sun:tools:jar must specify an absolute path but is ${tools.jar} in com.sun.xml.bind:jaxb-impl:2.2.11
BUILD SUCCESSFUL in 43s
47 actionable tasks: 37 executed, 4 from cache, 6 up-to-date最后显示
BUILD SUCCESSFUL
表示编译成功 -
然后导入到 idea 中(File -> New -> Project from Existing Sources -> Navigate to directory -> Select build.gradle),这里要等待 idea 导入。
源码测试
创建 gradle 模块
后面可能有多个测试模块,这里就再加一个子模块用于测试容器功能,最后建出来的模块结构如下:
在 test-context 模块中的 build.gradle 文件中增加依赖,新建的模块都在 Spring 项目中,因此可以通过
project(":spring-context")
直接引入同项目的模块:1
2
3dependencies {
implementation project(":spring-context")
}新建一个类
Hello.class
:1
2
3
4
5class Hello {
public String sayHello() {
return "hello spring";
}
}使用最原始的 xml 配置文件的方式配置 bean,在 resource 文件夹下新建 application-context.xml 文件,并配置 bean 信息:
1
2
3
4
5
6
7
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hello" class="com.phoegel.context.Hello"/>
</beans>新建测试方法
1
2
3
4
5
6
7public class Main {
public static void main(String[] args) {
BeanFactory context = new XmlBeanFactory(new ClassPathResource("application-context.xml"));
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.sayHello());
}
}整体代码结构如下
1
2
3
4
5
6
7
8
9
10
11
12
13public class Main {
public static void main(String[] args) {
BeanFactory context = new XmlBeanFactory(new ClassPathResource("application-context.xml"));
Hello hello = (Hello) context.getBean("hello");
System.out.println(hello.sayHello());
}
}
class Hello {
public String sayHello() {
return "hello spring";
}
}