spring学习总结002 --- IOC容器启动源码(简易版)

Posted sniffs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring学习总结002 --- IOC容器启动源码(简易版)相关的知识,希望对你有一定的参考价值。

技术图片

这张图是最最简单的处理流程图,其中还省略了初始化国际化、事件广播器等流程;下面参照ClassPathXmlApplicationContext源码,记录下IOC容器启动的大致流程:

1、ClassPathXmlApplicationContext构造器

public ClassPathXmlApplicationContext(
        String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
        throws BeansException {

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

先为IOC容器设置了配置文件路径,然后执行IOC启动最核心的方法:refresh()

2、refresh方法

refresh方法实现位于ClassPathXmlApplicationContext的父类AbstractApplicationContext,主要有如下几个功能:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 容器刷新的准备工作
        prepareRefresh();

        // 创建BeanFactory, 读取配置文件中bean的定义
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // 对BeanFactory进行初始化
        prepareBeanFactory(beanFactory);

        try {
            // 钩子方法, 子类进行扩展
            postProcessBeanFactory(beanFactory);

            // 执行BeanFactoryPostProcessor
            invokeBeanFactoryPostProcessors(beanFactory);

            // 注册BeanPostProcessor
            registerBeanPostProcessors(beanFactory);

            // 初始化国际化信息
            initMessageSource();

            // 初始化事件广播器
            initApplicationEventMulticaster();

            // 钩子方法, 子类进行扩展
            onRefresh();

            // 注册事件监听器
            registerListeners();

            // 初始化单例bean(未配置lazz-init)
            finishBeanFactoryInitialization(beanFactory);

            // 容器启动最后一步: 事件推送
            finishRefresh();
        }
    }
}

3、prepareRefresh方法

protected void prepareRefresh() {
    // 设置启动时间和启动标记
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);
    
    // 钩子方法, 用户自行实现初始化属性
    initPropertySources();

    // 验证所有必要属性, 必要属性来源:
    // 1、ConfigurablePropertyResolver#setRequiredProperties
    // 2、用户扩展的initPropertySources
    getEnvironment().validateRequiredProperties();

    // 允许应用启动之前的事件,当multicaster一旦可用的时候,可用立刻响应发布的事件。
    this.earlyApplicationEvents = new LinkedHashSet<>();
}

里面有一个钩子方法,由子类实现其功能;我在实际项目中使用initPropertySources,强制要求某些环境变量必须存在

示例:

public class MyAbstractApplicationContext extends ClassPathXmlApplicationContext {

    public MyAbstractApplicationContext(String configLocation) throws BeansException {
        super(configLocation);
    }

    @Override
    protected void initPropertySources() {
        super.initPropertySources();

        // 设置TEST_ENV环境变量为必要的, 如果没有, IOC容器启动失败
        getEnvironment().setRequiredProperties("TEST_ENV");
    }
}

运行结果:

技术图片

4、obtainFreshBeanFactory方法

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 创建BeanFactory, 然后解析并保存Bean定义
    refreshBeanFactory();

    // 返回BeanFactory
    return getBeanFactory();
}
protected final void refreshBeanFactory() throws BeansException {
    // 存在BeanFactory, 先销毁
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 创建BeanFactory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        // 定制BeanFactory, 根据Application的配置, 决定BeanFactory是否允许重复依赖和Bean名称重复
        customizeBeanFactory(beanFactory);
        // 解析并保存Bean定义信息
        // 保存在DefaultListableBeanFactory.beanDefinitionMap
        loadBeanDefinitions(beanFactory);
        // 设置为全局BeanFactory
        this.beanFactory = beanFactory;
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

customizeBeanFactory方法可以为BeanFactory设置是否允许bean循环依赖以及是否允许bean名称重复

以上是关于spring学习总结002 --- IOC容器启动源码(简易版)的主要内容,如果未能解决你的问题,请参考以下文章

Spring IOC 学习总结

spring学习总结007 --- IOC容器级生命周期接口

spring学习总结005 --- IOC容器启动源码(事件机制)

Spring全面详解(学习总结)

Spring——IOC容器基本概念

Spring——IOC容器基本概念