SpringIOC源码解析1(基于xml配置)
公司大神剖析了基于javabean方式容器初始化的底层源码,现在我们来剖析一下基于xml的底层源码
xml入口
package com.spring.ioc;
import com.spring.ioc.bean.XmlBean;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @program: IOC
* @description: 基于xml的创建bean的测试类
* @author: ZhengZhe
* @create: 2019-11-15 09:22
*/
public class XmlBeanTest {
public static void main(String[] args) {
//第一步我们肯定是需要引入我们创建的配置文件
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("xmlBean.xml");
//通过bean的实体类名字获取对应的 实体类
XmlBean xmlBean = (XmlBean) context.getBean("xmlBean");
//直接输出 对应的实体类
System.out.println(xmlBean.toString());
}
}
这种实现方式主要是通过我们自己配置的Spring文件进行的操作,通过读取xml配置文件,进而解析,获取配置文件中的bean,并进行初始化等操作
从上面代码中可以看到 我们new了一个对象用来进行容器的初始化操作,当我们点击进去后发现全是一层一层的继承关系,我找来了一张图展示这种关系
我们可以看到在经过了多层次的继承关系后 最终指向了BeanFactory接口
Spring Bean 的创建是典型的工厂模式,这一系列的 Bean 工厂,从而使IOC 容器为开发者管理对象间的依赖关系提供了很多便利和基础服务,在 Spring 中有许多的 IOC 容器的实现供用户选择和使用。如下为BeanFactory的关系结构图:
BeanFactory作为最*的接口,它定义了IOC容器最基本的规范。从结构图,我们可以看到:BeanFactory有三个子类,分别是HierarchicalBeanFactory 、AutowireCapableBeanFactory和ListableBeanFactory。但是他们最终的实现类工厂都是DefaultListableBeanFactory。既然所有的工作都由DefaultListableBeanFactory来完成,那为什么还要定义这么多层的接口呢?每个接口都有它的使用的场合,主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。例如 ListableBeanFactory 接口表示这些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个 Bean 有可能有父 Bean,AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。这几个接口就定义了Bean的集合、Bean之间关系、以及 Bean的行为。
BeanDefinition类
SpringIOC 容器管理了我们定义的各种 Bean 对象,以及Bean对象之间的相互关系。Bean对象以及它们之间的关系,我们是通过xml文件来配置的。IOC既然要帮助我们完成对象的创建以及每个对象之间的关系,那么IOC容器则需要通过读取xml配置文件来获取具体配置的信息。
对xml解析时,在Spring源码中,对xml文件中bean的配置,则是使用BeanDefinition类来表示的。
Bean的解析过程相对来说比较复杂。它会对Spring xml配置文件中的所有配置信息进行解析,包括常见的、、、标签进行解析,还会对所有标签中的所有属性进行解析。针对每个标签以及部分属性等,都是单独定义一个方法来完成这个操作,功能上来说,分的比较细致,因为它要保证Spring框架的可扩展性,保证灵活性。
IOC容器的初始化
- 定位
- 载入
- 注册
- 定位
通过用户配置的信息,使用classpath/filesystem/url链接等加载文件方式,获取到对应的文件资源,最终将其解析成一个Resource类型文件的过程,XmlBeanDefinitionReader通过调用其父类DefaultResourceLoader的getResource()方法获取要加载的资源
定位步骤:
获取配置文件名称------>通过不同类型的resourceLoader加载器,加载文件------>调用 的是 DefaultResourceLoader 中的 getSource()方法定位 Resource----->获取Resource类型文件(内容实际是获取自己配置xml的信息)
2.载入
即通过对应为获取到的xml文件,进行每一步细致的解析,然后获取到BeanDefinition类的过程.
SpringIOC容器对Bean定义资源的载入是从refresh()函数开始的,refresh()是一个模板方法.
使用resourceLoader.getResource(location)获取要加载的资源------>使用JAXP将Resource类型的xml文件流转换成为Document对象------>根据Document对象获取所有的node子节点------>根据子节点来判断,是否是import标签、alias标签、bean标签、beans标签------>针对每个不同的标签以及标签中的不同属性参数,使用不同的方法做不同的处理------>将xml文件中所有的配置的bean信息设置到BeanDefination类中作记录保存
3.注册
即:将BeanDefinition类,根据key(key可以是xml文件中配置的id、name、alias等唯一的属性,),put到Map的过程。从注册这个过程,你会发现:IOC容器其实就是一个Map(具体是一个ConcurrentMap)
注册的过程中,使用了synchronized锁,保持线程的同步,以保证数据的一致性------>将BeanDefinition类中的id属性/name属性/alias属性等来充当key,将BeanDefinition put到一个Map中,就表示注册完成
IOC容器初始化,定位完成后。会读入Bean定义的资源,此时会调用一个refresh()方法,来保证IOC容器必须是唯一的。
refresh()方法的作用是:refresh()方法,使用synchronized修饰。在创建 IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在 refresh 之后使用的是新建立起来的 IOC 容器。refresh 的作用类似于对 IOC 容器的重启, 在新建立好的容器中对容器进行初始化,对 Bean 定义资源进行载入。
refresh()方法------主要是obtainFreshBeanFactory()方法
//1.容器初始化的过程,读入Bean定义资源
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
prepareRefresh();
//告诉子类启动 refreshBeanFactory()方法,Bean 定义资源文件的载入从子类的 refreshBeanFactory()方法启动
//*********注意:此处使用委派模式,委派子类完成对IOC容器唯一性判断!!!(如下附obtainFreshBeanFactory()方法)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//为 BeanFactory 配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
try {
//为容器的某些子类指定特殊的 BeanPost 事件处理器
postProcessBeanFactory(beanFactory);
//调用所有注册的 BeanFactoryPostProcessor 的 Bean
invokeBeanFactoryPostProcessors(beanFactory);
//为 BeanFactory 注册 BeanPost 事件处理器.BeanPostProcessor 是 Bean 后置处理器,用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);
//初始化信息源,和国际化相关.
initMessageSource();
//初始化容器事件传播器.
initApplicationEventMulticaster();
//调用子类的某些特殊 Bean 初始化方法
onRefresh();
//为事件传播器注册事件监听器.
registerListeners();
//初始化所有剩余的单例 Bean.
finishBeanFactoryInitialization(beanFactory);
//初始化容器的生命周期事件处理器,并发布容器的生命周期事件
finishRefresh();
}catch (BeansException ex) {
//销毁以创建的单态 Bean
destroyBeans();
//取消 refresh 操作,重置容器的同步标识.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
obtainFreshBeanFactory()方法
AbstractApplicationContext的obtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory() 方法,启动容器载入 Bean 定义资源文件的过程,代码如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//这里使用了委派设计模式,父类定义了抽象的 refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
子类容器的具体实现refreshBeanFactory()方法
AbstractApplicationContext 类中只抽象定义了 refreshBeanFactory()方法,容器真正调用的是 其子类AbstractRefreshableApplicationContext实现的refreshBeanFactory()方法,源码如下:
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {{//如果已经有容器,销毁容器中的 bean,关闭容器
destroyBeans();
closeBeanFactory();
}
try {
//创建 IOC 容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//对 IOC 容器进行定制化,如设置启动参数,开启注解的自动装配等
customizeBeanFactory(beanFactory);
//调用载入 Bean 定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的 loadBeanDefinitions 方法,具体的实现调用子类容器
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
下面给出基于AnnotationConfigApplicationContext的源码解析链接 :SpringIOC源码解析2(基于AnnotationConfigApplicationContext)
推荐阅读
-
SpringIOC源码解析1(基于xml配置)
-
jsp基于XML实现用户登录与注册的实例解析(附源码)
-
mybatis plus源码解析(一) ---基于springboot配置加载和SqlSessionFactory的构造
-
Spring源码学习-12(基于xml的ioc源码解析)
-
Spring集成Mybatis源码分析(三)-配置文件初始化及XML解析
-
jsp基于XML实现用户登录与注册的实例解析(附源码)
-
[nginx源码分析]配置解析1
-
Spring源码学习-12(基于xml的ioc源码解析)
-
Zeppelin的求学之路(1)—— 简介 和 从源码Build的安装并配置他的Spark解析器
-
基于XML配置的SpringIOC案例