spring源码阅读之bean加载过程(一)
如果想要阅读源码,首先要选择版本,然后将源代码下载到本地,导入idea中,话不多说,直接看步骤吧
这里我选择5版本,
下载源码
默认是main分支,看想学习的分支,比如我切换到5版本,截图如下:
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-22-18-33TlskTzHHkRzzGFk.png)
2.安装gradle
3.转换源码进idea
下载完成后可以看到有个文档叫import-into-idea.md,这里介绍了怎么将代码导进idea,不过对我这种英语不好的真的是费劲,可以参考https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Spring/%E6%B7%B1%E5%85%A5Spring%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%E5%9C%A8IDEA%E4%B8%AD%E6%9E%84%E5%BB%BASpring%E6%BA%90%E7%A0%81.md
查看spring与gradle的版本对应,目录:spring-framework/gradle/wrapper/gradle-wrapper.properties
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-15-13-02ipKZa2PX9lBDkXz.png)
最好是要保持版本一致,否则可能下面的命令执行报错
默认每次都会下载gradle,现改成本地的文件即可
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-15-13-02m7IC7z5Dn88sCco.png)
编译spring-oxm(jdk8报错,后来改成jdk21可以了,网上有说11也可以,可以自行实验,本人没有验证过,刚好在本地有21版本,直接拿来用了),由于开发使用1.8所以我就没有改系统默认jdk版本,先导入idea,在idea里面操作了,最终编译成功,截图如下
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-15-13-0215vKAA1rXQfFDuuf.png)
可以看到spring源码现在不报错了
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-15-13-02Udlb6lWBUhADUO0.png)
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-15-13-02f2gypUSOKMzv0h6.png)
可以看到就像是我们自己的项目一样,可以看到提交记录和提交人等相关信息,也可以直接修改
spring源码已下载到本地了,我最想了解的是bean的加载过程,然后点到代码里可以看到测试用例非常全面,试运行了一下,确实可以运行截图如下
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-15-13-02dOEq1HnKdAMOqP6.png)
接下来正式进入源码阅读时间
下面要看的测试用例为:
org.springframework.beans.factory.FactoryBeanTests#testFactoryBeanReturnsNull,代码如下
@Test
void testFactoryBeanReturnsNull() {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(factory).loadBeanDefinitions(RETURNS_NULL_CONTEXT);
assertThat(factory.getBean("factoryBean").toString()).isEqualTo("null");
}
先假设如果是自己的系统要做一个类似的获取bean的功能的话我会怎么设计:
1.解析xml,生成bean
2.将生成的bean存到一个地方
3.为了获取方便,大概率我会写一个map,通过bean的id可以获取数据
以下是spring的类图
spring源码阅读之bean加载过程(一)
如果想要阅读源码,首先要选择版本,然后将源代码下载到本地,导入idea中,话不多说,直接看步骤吧
这里我选择5版本,
下载源码
默认是main分支,看想学习的分支,比如我切换到5版本,截图如下:
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-22-18-44j7sTunp6EFrTgIu.png)
2.安装gradle
3.转换源码进idea
下载完成后可以看到有个文档叫import-into-idea.md,这里介绍了怎么将代码导进idea,不过对我这种英语不好的真的是费劲,可以参考https://github.com/coderbruis/JavaSourceCodeLearning/blob/master/note/Spring/%E6%B7%B1%E5%85%A5Spring%E6%BA%90%E7%A0%81%E7%B3%BB%E5%88%97%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%E5%9C%A8IDEA%E4%B8%AD%E6%9E%84%E5%BB%BASpring%E6%BA%90%E7%A0%81.md
查看spring与gradle的版本对应,目录:spring-framework/gradle/wrapper/gradle-wrapper.properties
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-15-13-02CaKjSlg1yRI8AZw.png)
最好是要保持版本一致,否则可能下面的命令执行报错
默认每次都会下载gradle,现改成本地的文件即可
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-22-18-45YbyhCDrzQyKC9KR.png)
编译spring-oxm(jdk8报错,后来改成jdk21可以了,网上有说11也可以,可以自行实验,本人没有验证过,刚好在本地有21版本,直接拿来用了),由于开发使用1.8所以我就没有改系统默认jdk版本,先导入idea,在idea里面操作了,最终编译成功,截图如下
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-15-13-020wF1zMVWPwFzu1J.png)
可以看到spring源码现在不报错了
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-15-13-02fbL55NwgVtWOcZC.png)
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-15-13-02a15n7kdrBH9uAU1X.png)
可以看到就像是我们自己的项目一样,可以看到提交记录和提交人等相关信息,也可以直接修改
spring源码已下载到本地了,我最想了解的是bean的加载过程,然后点到代码里可以看到测试用例非常全面,试运行了一下,确实可以运行截图如下
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-15-13-02LLFCGFZn9HgoJtP.png)
接下来正式进入源码阅读时间
下面要看的测试用例为:
org.springframework.beans.factory.FactoryBeanTests#testFactoryBeanReturnsNull,代码如下
@Test
void testFactoryBeanReturnsNull() {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
new XmlBeanDefinitionReader(factory).loadBeanDefinitions(RETURNS_NULL_CONTEXT);
assertThat(factory.getBean("factoryBean").toString()).isEqualTo("null");
}
先假设如果是自己的系统要做一个类似的获取bean的功能的话我会怎么设计:
1.解析xml,生成bean
2.将生成的bean存到一个地方
3.为了获取方便,大概率我会写一个map,通过bean的id可以获取数据
以下是spring的类图
![](https://s3.cn-north-1.jdcloud-oss.com/shendengbucket1/2024-05-15-13-04lDJ5TrJP8bZbCVj.jpeg)
类图1
具体分析:
上图看起来足够复杂,仔细分析之后发现我们写的那几步都包含在里面而已
黄色:用来存储bean的实体,最上层的类为BeanDefinition,抽象类AbstractBeanDefinition,在本测试用例中使用的是工具类org.springframework.beans.factory.support.BeanDefinitionReaderUtils#createBeanDefinition生成的GenericBeanDefinition
public static AbstractBeanDefinition createBeanDefinition(@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
} else {
bd.setBeanClassName(className);
}
}
return bd;
}
粉色:主要功能就是从Document中解析出来各个bean及相应的attribute,properties等,解析完之后交给工具类生成BeanDefinition
主要代码如下:
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//设置bean的属性