zoukankan      html  css  js  c++  java
  • spring学习总结002 --- IOC容器启动源码(BeanFactory)

    这张图是最最简单的处理流程图,其中还省略了初始化国际化、事件广播器等流程;下面参照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名称重复

    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        // 是否允许覆盖重复bean定义
        if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        // 是否允许循环依赖
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }

    spring有如下的行为:如果一个bean配置文件中存在两个及以上id或者name相同的bean,name直接抛出异常;如果不同配置文件中存在两个及以上id或者name相同的bean,默认会进行覆盖处理(可配置);

    bean重复定义检查的流程在BeanFactory解析BeanDefination中,至于循环依赖,在另外的文章中说明。

    不允许bean重复简单示例

    public static void main(String[] args) {
        MyAbstractApplicationContext applicationContext = new MyAbstractApplicationContext("classpath:bean5.xml", "classpath:bean6.xml");
    
        applicationContext.setAllowBeanDefinitionOverriding(false);
    
        applicationContext.refresh();
    
        Name name = (Name) applicationContext.getBean("name");
    
        log.info(name.toString());
    }

    运行结果:

    5、prepareBeanFactory

    BeanFactory的准备阶段,这块没细看,只是知道忽略了生命周期相关接口的bean定义

    6、postProcessBeanFactory

    钩子方法,由子类实现;该方法用于在BeanFactory创建后对BeanFactory做一些定制化,如忽略某些接口的自动装配

    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    }
  • 相关阅读:
    5
    4
    3
    crontab -e 报错(E518: Unknown option: foldenable)
    解决无法修改日志时间的问题(Local time zone must be set--see zic manual page 2019 )
    ping测试丢包率
    关闭SELinux
    iotop使用方法
    mysql的备份
    修改uid gid 的起始范围
  • 原文地址:https://www.cnblogs.com/sniffs/p/13229969.html
Copyright © 2011-2022 走看看