您现在的位置是:首页 >技术杂谈 >Spring : XML配置 JavaBean源码解析网站首页技术杂谈

Spring : XML配置 JavaBean源码解析

无奈朝来寒雨晚来风 2024-06-17 11:28:30
简介Spring : XML配置 JavaBean源码解析


前言

跟着大佬走!!!! https://github.com/DerekYRC/mini-spring

在这里插入图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、xml 加载 Bean 对象

大家先了解一下这个图,是作者给的定义

在这里插入图片描述

正片开始

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:factory-bean.xml"); 获取一个应用程序上下文对象,里面加载了xml 文件,最后将 xml 文件的对象取出,这里对应我们刚开始学 Spring 使用 xml 配置bean 对象。

在这里插入图片描述
在这里插入图片描述

接下来我们一步步 deBug 深入大佬源码。我会先上图再给注释,上图下描述,DeBug 走起。

在这里插入图片描述

在这里插入图片描述

先来到集成图的最下面 ClassPathXmlApplicationContext ,这里调用有参构造 ClassPathXmlApplicationContext 传入了xml文件名,然后把 classpath:factory-bean.xml 转换成了一个 String 数组,扔给了另一个重载的 有参构造。

在这里插入图片描述

到了个有参构造,把 configLocations 文件名数组,赋值给了全局变量 this.configLocations (也是一个字符串数组)。然后会调用 refresh( ) 方法,

在这里插入图片描述
在这里插入图片描述

refresh( ) 会来到 AbstractApplicationContext , 会先调用 refreshBeanFactory() 方法刷新 bean 工厂,所有会去到子类。

在这里插入图片描述
在这里插入图片描述

来到子类 AbstractRefreshableApplicationContext 的 refreshBeanFactory() 方法, 会先调用 createBeanFactory(); 创建默认的 bean 工厂,然后将 工厂扔给了 loadBeanDefinitions(beanFactory); 方法,加载 bean定义。

DefaultListableBeanFactory beanFactory = createBeanFactory(); 调用本类方法,创建默认的 bean 工厂

在这里插入图片描述

return new DefaultListableBeanFactory(); 会来到 DefaultSingletonBeanRegistry

在这里插入图片描述
在这里插入图片描述

由于 DefaultListableBeanFactory 集成了三个类,这里要对继承的类进行初始化操作,子类初始化,要先将父类进行加载。

在这里插入图片描述

拥有了默认的 bean 工厂后,将工厂扔给 本类的 loadBeanDefinitions(beanFactory); ,但是这是一个抽象方法,还是要到子类进行查看。

在这里插入图片描述

值得我们注意的是,这个方法还是抽象方法,由子类进行实现。我们还要跟着去子类,但是这一跟就会回到一开始 debug 的地方。

在这里插入图片描述在这里插入图片描述

XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory, this); 会去创建 一个 XmlBeanDefinitionReade 对象

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

总结XML加载Bean对象

首先我们先记住三个继承图在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

XML文件配置JavaBean要从一个 new ClassPathXmlApplicationContext("classpath:factory-bean.xml");开始读取XML文件。这个最终继承到 AbstractApplicationContext ,当我们传入xml文件时,会先将 文件名转换成一个数组,调用ClassPathXmlApplicationContext的重载方法,传入这个数组,然后依次加载父类,AbstractXmlApplicationContext, AbstractRefreshableApplicationContext, AbstractApplicationContext , DefaultResourceLoader, 初始化子类需要先加载父类,然后将文件数组赋值给成员变量 private String[] configLocations; 以便于后续获取这个文件名。

然后会调用 refresh(); 方法,这个方法会调用到 AbstractApplicationContext 的 refresh(); 方法 然后执行 refreshBeanFactory(); //创建BeanFactory,并加载BeanDefinition 但是这是个抽象方法 abstract ,需要到子类AbstractRefreshableApplicationContext去查看,就会到 AbstractRefreshableApplicationContext 对象的 refreshBeanFactory() 方法,会获取到一个 DefaultListableBeanFactory 对象, 默认的Bean工厂。

然后就是创建默认 Bean工厂(第二张继承图),这时由于 DefaultListableBeanFactory 还是最底层的类,初始化之前,还要去初始化父类,父类依次是 AbstractAutowireCapableBeanFactory, AbstractBeanFactory , DefaultSingletonBeanRegistry ,到 DefaultSingletonBeanRegistry 会依次加载 Spring 的三级缓存对于Spring的三级缓存,可自行搜索了解,和一个任意处理的 Bean容器 Map<String, DisposableBean> disposableBeans。然后一路返回到 DefaultListableBeanFactory 默认Bean工厂,返回的路上还会初始化父类的成员属性。
然后会调用 loadBeanDefinitions(beanFactory); 把这个默认的 bean工厂扔进去,很不巧的是,这个还是个抽象方法,就会到 AbstractRefreshableApplicationContext 的子类 AbstractXmlApplicationContext

来到 AbstractXmlApplicationContext 的 loadBeanDefinitions()方法,会得到一个 XmlBeanDefinitionReader 对象(第三张继承图),这个对象用来整个前两个继承图的两个体系,在 new XmlBeanDefinitionReader(beanFactory, this); 会传入两个参数,一个是默认的 bean工厂 DefaultListableBeanFactory , this 是谁调用就是谁,我们是从 ClassPathXmlApplicationContext 进来的,所以这个 this是ClassPAC ,这样就把两个继承体系整合在了一起。然后调用 getConfigLocations() 方法获取文件名,很不巧这个方法仍然是抽象方法,会去到子类 ClassPathXmlApplicationContext 正是我们一开始 DeBUG 的地方,设计的非常巧妙。

然后调用 XmlBeanDefinitionReader . loadBeanDefinitions(xml文件名 ) 方法, Xmlbean定义读取器加载 xml 文件,然后调用到父类 AbstractBeanDefinitionReader 的loadBeanDefinitions() 方法遍历出文件名。很不巧的是 loadBeanDefinitions 是重载方法,会再次重载到 XmlBeanDefinitionReader . loadBeanDefinitions( ) 这个地方重载的也非常妙。然后就是 getResourceLoader(); 之前 new XmlBeanDefinitionReader ,时传入了一个 ClassPathXmlApplicationContext 这个对象间接实现了 ResourceLoader,所以这里 getResourceLoader()会获取到 ClassPathXmlApplicationContext ;只不过这里使用 ResourceLoader 进行接收,然后调用 getResource (location文件名),后面会将这个文件名封装到一个 InputStream 读取xml 文件,然后调用 resource.getInputStream(); 获取到这个 InputStream 流。

最后到 doLoadBeanDefinitions(InputStream inputStream) 真正执行程序加载 xml 文件, //解析context:component-scan标签并扫描指定包中的类,提取类信息,组装成BeanDefinition , 标签属性 //id优先于name, //如果id和name都为空,将类名的第一个字母转为小写后作为bean的名称 , //beanName不能重名

//注册BeanDefinition
getRegistry().registerBeanDefinition(beanName, beanDefinition);

最后将解析xml 得到的 bean 放到 DefaultListableBeanFactory 默认bean工厂的 beanDefinitionMap 。

    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
		beanDefinitionMap.put(beanName, beanDefinition);
	}

至此 XML 文件配置 Java Bean 结束

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。