环境准备:
使用spring5.1.6版本
1 xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <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="user" class="com.hou.spring.bean.User"></bean> </beans>
2 测试类
public class BeanTest{ @Test public void beanTest(){ //spring4之后XmlBeanFactory被废弃,改用以下方式 ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-bean.xml"); User user = (User) applicationContext.getBean("user"); System.out.println(user); } }
然后点进去源码,跟着一步步debug来分析:
1 构造器调用:
ClassPathXmlApplicationContext的构造器中调用类同名方法:
点击this跳转到初始化方法:
2 super()方法是一直到父类AbstractApplicationContext中,将ApplicationContext的环境属性设置给本类的环境属性,包括一些profile,系统属性等
3 setConfigLocations方法也是调用父类方法,将xml配置文件名字设置给父类的String数组属性
4 refresh() 方法,所有的逻辑其实都在这个方法里面进行,主要分析这个方法:
5 prepareRefresh主要还是环境属性的一些初始化,主要看第二步:
// Tell the subclass to refresh the internal bean factory. 告诉子类刷新内部bean工厂 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
点进去obtainFreshBeanFactory:
6 首先看refreshBeanFactory方法,注意,如果不知道是哪个子类的话,可以跟着debug断点走:
主要分为这么几个步骤:
一 首先判断本类的DefaultListableBeanFactory属性是否为null,如果不为null,就先清除一写跟Bean有关的Map或者List等属性集合
二 将BeanFactory设置为null,序列化id设置为null,
三 创建DefaultListableBeanFactory,这个类很重要,是springBean初始化的核心类,
四 对beanFactory进行设置,bean注册等操作,最后将beanFactory赋值给本类的beanFactory属性
7 customizeBeanFactory(beanFactory); 只做了两件事:
8 loadBeanDefinitions: Bean的注册主要是在这一步进行,下面进行分析,这个方法有5个子类实现:
我写的测试类不是Web项目,所以会进入AbstractXmlApplicationContext这个类里的方法,如果是Web项目,会走XmlWebApplicationContext:
首先创建XmlBeanDefinitionReader:xml配置读写器然后设置环境属性以及资源加载器为ClassPathXmlApplicationContext,这个加载器很重要,后面会用到
接着初始化读取器: initBeanDefinitionReader,最后加载Bean
8 initBeanDefinitionReader
这个方法默认实现是空的,允许用户自定义实现读取器的定制化,需要实现接口,可以设置xml解析完成校验,定制化解析器等
9 loadBeanDefinitions: 加载Bean信息,点进去:
这个方法主要是加载类的两个资源属性,Resource[] 和xml位置信息,主要看加载Xml的:
10 reader.loadBeanDefinitions(configLocations);
循环加载xml文件的Bean返回Bean总个数,查看加载方法:
11 查看这个load方法:
这里需要注意第八步设置的加载器,查看加载器的时序图:
因为有继承关系所以直接进if分支,继续分析if分支代码:
主要步骤:
1 获取加载器中的Resource[] 数组
2 加载资源中的Bean,返回加载数量
12 查看loadBeanDefinitions,循环加载了所有的资源,返回总数
13 查看单个加载方法loadBeanDefinitions,主要看中间一段逻辑:
这里对正在解析的xml资源放入ThreadLocal中,保证只有本次线程可以访问,加载完之后再移除
13 查看doLoadBeanDefinitions(inputSource, encodedResource.getResource());