Spring IoC(二)IoC容器的初始化过程

(一)IoC 容器初始化过程概述

1.1简要概述初始化过程

IoC 容器的初始化过程是通过refresh() 方法来启动的,这个方法标识着IoC 容器正式启动。具体来说,这个启动过程包括:BeanDefinition 的Resource 定位、载入和注册三个基本过程。

Spring 把这三个过程分开,并使用不同的模块来完成,通过这样的设计方式,可以让用户更加灵活的对这三个过程进行裁剪或扩展,从而方便自己定义IoC 容器的初始化过程。

第一个过程是Resource 的定位过程。这个Resource 的定位指的是BeanDefinition 的资源定位,它由ResourceLoader 通过统一的Resource 接口来完成。对于BeanDefinition 的存在形式,可以是文件系统中的,通过FileSystemResource 来进行抽象;类路径中定义的Bean 信息可以通过ClassPathResource 来进行抽象。等等。这个过程可以理解为IoC 容器寻找数据的过程。

第二个过程是BeanDefinition 的载入。这个载入过程是把用户定义好的Bean 定义成IoC 容器内部的数据结构,而这个容器内部数据结构就是BeanDefinition。具体来说,这个BeanDefinition 实际上就是POJO 对象在IoC 容器中的抽象,通过这个BeanDefinition 定义的数据结构,使IoC 容器能够方便地管理Bean。

第三个过程是向IoC 容器中注册这些BeanDefinition 的过程。这个过程是通过BeanDefinitionRegistry 接口的实现来完成的。这个注册过程把载入过程中解析得到的BeanDefinition 向IoC 容器进行注册。在IoC 容器内部将BeanDefinition 注入到一个HashMap 中去,IoC 容器就是通过这个HashMap 来持有这些Bean 数据的。

这里谈的IoC 容器的初始化过程,并不包含Bean 依赖注入的实现。在Spring IoC 的设计中,Bean 定义的载入和依赖注入是两个独立的过程。依赖注入一般发生在第一个通过getBean() 向容器中获取Bean 的时候。但是也并不全是这样,比如我们对某个Bean 设置了lazyinit 属性,那么Bean 的依赖注入在其初始化的过程中就已经完成了。

(二)IoC 容器具体初始化过程解析

2.1BeanDefinition 的Resource 定位

ApplicationContext 是一个接口,其实现类主要有:FileSystemXmlApplicationContext、
ClassPathXmlApplicationContext 和XmlWebApplicationContext 等。从这些类的名字就可以分析的出其加载文件的位置。FileSystemXmlApplicationContext 可以从文件系统中载入Resource、ClassPathXmlApplicationContext 可以从类路径下载入Resource、XmlWebApplicationContext 可以从Web 容器中载入Resource。

这里以FileSystemXmlApplicationContext 为例,来分析ApplicationContext 的实现是如何完成Resource 定位的,下面是这个类对应的继承体系。
这里写图片描述
下面是更为详细的继承体系。
这里写图片描述
从上图中可以看出FileSystemXmlApplicationContext 已经通过继承AbstractApplicationContext 具备了ResourceLoader 的功能,因为AbstractApplicationContext 继承自DefaultResourceLoader。下面是其源代码:

public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {

    public FileSystemXmlApplicationContext() {}

    public FileSystemXmlApplicationContext(ApplicationContext parent) {
        super(parent);
    }

    /**
     * 
     * @param configLocation 表示BeanDefinition 所在的文件路径
     * @throws BeansException
     */
    public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }

    /**
     * 
     * @param configLocations 表示BeanDefinition 文件数组所在的路径
     * @throws BeansException
     */
    public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, null);
    }

    /**
     * 
     * @param configLocations 可以包含多个BeanDefinition 的文件路径
     * @param parent 指定自己的IoC 双亲容器
     * @throws BeansException
     */
    public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
        this(configLocations, true, parent);
    }

    /**
     * 
     * @param configLocations 文件数组
     * @param refresh 是否自动刷新上下文
     * @throws BeansException
     */
    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
        this(configLocations, refresh, null);
    }

    public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
            throws BeansException {

        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

    /**
     * 文件系统中Resource 的实现,通过一个FileSystemResource 来得到一个在文件系统中定位的BeanDefinition
     * @param path 文件路径
     * @return
     */
    @Override
    protected Resource getResourceByPath(String path) {
        if (path != null && path.startsWith("/")) {
            path = path.substring(1);
        }
        return new FileSystemResource(path);
    }

}

对BeanDefinition 资源定位的过程,最初是由refresh() 方法触发的,refresh() 方法的调用是在
FileSystemXmlApplicationContext 的构造函数中启动的。getResourceByPath() 方法的调用过程主要如下:
这里写图片描述

从上面的源代码中可以看出,只要是以XML 文件方式存在的BeanDefinition 都能够得到有效的处理。上面的源代码中并没有涉及到BeanDefinition 的信息读入,那么FileSystemXmlApplicationContext 是怎么完成信息的读入的呢?
上面已经提到过BeanDefinition 资源的定位、载入和注册过程都是分开进行的。关于这个读入器的配置,可以在FileSystemXmlApplicationContext 的基类AbstractRefreshableApplicationContext 中查看。

这里主要看AbstractRefreshableApplicationContext 中的refreshBeanFactory() 方法的实现,
这个方法被FileSystemXmlApplicationContext 构造函数中的refresh() 方法所调用。在refreshBeanFactory()这个方法中,通过createBeanFactory() 创建一个IoC 容器供AppliCationContext 使用。这个IoC 容器就是上一篇博文中提到的DefaultListableBeanFactory。同时,还启动了loadBeanDefinitions() 来载入BeanDefinition。下面是refreshBeanFactory() 的方法的源码:

 @Override
    protected final void refreshBeanFactory() throws BeansException {
        // 如果已经建立了BeanFactory,则销毁并关闭
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
        try {
            // 通过createBeanFactory() 方法获得一个创建的IoC 容器
            // 创建的是DefaultListableBeanFactory 容器
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            customizeBeanFactory(beanFactory);
            // 载入BeanDefinition
            loadBeanDefinitions(beanFactory);
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        }
        catch (IOException ex) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

在BeanDefinition 定位完成的基础上,就可以通过返回的Resource 对象来进行BeanDefinition 的载入了。在完成定位过程后,为BeanDefinition 的载入创造了I/O 操作的条件,但是具体的数据还没有开始读入。

2.2BeanDefinition 的载入和解析

先发出来,慢慢更!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值