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容器的初始化
  • 定位
  • 载入
  • 注册
  1. 定位
    通过用户配置的信息,使用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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值