spring源码系列(十): 读取xml入口类 ClassPathXmlApplicationContext 分析

环境准备:

使用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());

 

 

 


posted @ 2019-07-15 15:04  侯小厨  阅读(1324)  评论(0编辑  收藏  举报
Fork me on Gitee