• 04.Spring Ioc 容器


    基本概念

    Spring Ioc 容器被创建之后,接下来就是它的初始化过程了。该过程包含了配置、刷新两个步骤 

    刷新由 Spring 容器自己实现,具体发生在 ConfigurableApplicationContext 的 refresh 方法中。

    首先来看该接口的继承关系:

    Alt text

    由于这里 Spring 的配置文件采用 xml 形式,所以 Ioc 容器的类型默认为 XmlWebApplicationContext。接下来就以该类的 refresh 方法为入口,探究下 Ioc 容器的刷新过程。


    原理分析

    该类的 refresh 方法继承自己父类 AbstractApplicationContext,观察代码可以发现该方法包含了众多方法,这里只探究下 1、2 过程。

    private final Object startupShutdownMonitor = new Object();
    
    public void refresh() throws BeansException, IllegalStateException {
        // 加锁
        synchronized (this.startupShutdownMonitor) {
            // 1.准备刷新
            prepareRefresh();
    
            // 2.创建内部容器,该容器负责 Bean 的创建与管理 
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
            prepareBeanFactory(beanFactory);
    
            try {
                postProcessBeanFactory(beanFactory);
    
                invokeBeanFactoryPostProcessors(beanFactory);
    
                registerBeanPostProcessors(beanFactory);
    
                initMessageSource();
    
                initApplicationEventMulticaster();
    
                onRefresh();
    
                registerListeners();
    
                finishBeanFactoryInitialization(beanFactory);
    
                finishRefresh();
    
            } catch (BeansException ex) {
                // 省略代码...
    
                destroyBeans();
    
                cancelRefresh(ex);
    
                throw ex;
    
            } finally {
                resetCommonCaches();
            }
        }
    }

    准备刷新

    初始化准备,即为 Ioc 容器的初始化过程做准备,该过程与 Spring 的 Environment 有关,这里暂不探究。

    // 容器早期发布事件集
    private Set<ApplicationEvent> earlyApplicationEvents;
    
    protected void prepareRefresh() {
        // 省略部分代码...
    
        // 1.激活容器
        this.active.set(true);
    
        // 2.初始化 Environment 的 propertySources 属性 
        initPropertySources();
    
        // 3.校验 Environment 的 requiredProperties 是否都存在
        getEnvironment().validateRequiredProperties();
    
        // 4.创建事件集合
        this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
    }

    来看下 initPropertySources 这个方法, 它与 Spring 容器的 Environment 有关:

    protected void initPropertySources() {
        // 取得配置过程中创建的容器环境
        ConfigurableEnvironment env = getEnvironment();
    
        // 初始化 Environment 的 propertySources 属性
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).
                initPropertySources(this.servletContext, this.servletConfig);
        }
    }

    创建内部容器

    之前介绍过 ApplicationContext 对象里面会包含了一个 BeanFactory 对象。有关 Bean 的基本功能是调用内部的 BeanFactory 对象来实现的。

    在这里, BeanFactory 具体指的是 ConfigurableListableBeanFactory ,来看它们的继承关系:

    Alt text


    下面来看 obtainFreshBeanFactory 的具体实现:

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    
        // 1.刷新 BeanFactory
        refreshBeanFactory();
    
        // 2.取得 BeanFactory
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    
        // 省略部分代码...
    
        return beanFactory;
    }

    1.刷新 BeanFactory

    下面来看刷新内部 BeanFactory 的实现过程:

    protected final void refreshBeanFactory() throws BeansException {
    
        // 1.判断是否存在 Beanfactory ,存在则销毁并关闭它
        if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        }
    
        try {
            // 2.创建 BeanFactory
            DefaultListableBeanFactory beanFactory = createBeanFactory();
    
            // 3.设置 BeanFactory 的 id,同它外部的 Spring 容器一致
            beanFactory.setSerializationId(getId());
    
            // 4.定制 BeanFactory 初始化,暂不探究
            customizeBeanFactory(beanFactory);
    
            // 5.加载 Bean 实例
            loadBeanDefinitions(beanFactory);
    
            // 6.将 BeanFactory 设置为 Spring 容器的内部 BeanFactory 
            synchronized (this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
    
        }catch (IOException ex) {
            // 抛出异常...
        }
    }

    ①判断是否存在 BeanFactory,存在则销毁、关闭容器的内部 BeanFactory

    // 1.判断是否存在存在内部 BeanFactory
    protected final boolean hasBeanFactory() {
        synchronized (this.beanFactoryMonitor) {
            return (this.beanFactory != null);
        }
    }
    
    // 2.若存在销毁内部 BeanFactory 的所有 Bean 
    protected void destroyBeans() {
        getBeanFactory().destroySingletons();
    }
    
    // 2.关闭 内部 BeanFactory 
    protected final void closeBeanFactory() {
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory.setSerializationId(null);
            this.beanFactory = null;
        }
    }

    ②创建 BeanFactory,这里会默认创建 DefaultListableBeanFactory 类作为内部容器(内部 BeanFactory)。

    protected DefaultListableBeanFactory createBeanFactory() {
        // 1.取得内部 BeanFactory 的父容器 
        // 2.创建内部 BeanFactory
        return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    }
    
    
    protected BeanFactory getInternalParentBeanFactory() {
        // 判断取得 Spring 容器的父容器类型
        // 匹配,则返回父容器内部 BeanFactory,否则返回父容器
        return (getParent() instanceof ConfigurableApplicationContext) ?
            ((ConfigurableApplicationContext) getParent()).getBeanFactory() :getParent();
    }
    
    
    public DefaultListableBeanFactory(BeanFactory parentBeanFactory) {
        super(parentBeanFactory);
    }
    public AbstractAutowireCapableBeanFactory(BeanFactory parentBeanFactory) {
        this();
        setParentBeanFactory(parentBeanFactory);
    }

    Alt text


    ③加载 Bean 实例,该过程由 BeanDefinitionReader 负责,下篇在详细分析。


    2.取得 BeanFactory

    // 取得 BeanFactory
    public final ConfigurableListableBeanFactory getBeanFactory() {
        synchronized (this.beanFactoryMonitor) {
            if (this.beanFactory == null) {
                // 抛出异常...
            }
            return this.beanFactory;
        }
    }

    总结

    下面通过流程图来总结下 Spring Ioc 容器的初始化过程:

    Alt text

  • 相关阅读:
    struts2接收参数的几种形式
    oracle merge into函数中插入clob字段
    程序员能力矩阵
    spring mvc工作原理
    struts2核心和工作原理
    mysql主从复制(windows下)
    mysql主从复制(linux下)
    spring 注解事务
    异常错误集锦
    Redis 作为缓存服务器的配置
  • 原文地址:https://www.cnblogs.com/moxiaotao/p/9349506.html
Copyright © 2020-2023  润新知