zoukankan      html  css  js  c++  java
  • Spring源码阅读 之 bean的注册

    在前面我们已经学习了配置的加载,读取,解析。现在我们已经能够将一份配置转变成对应的一个个beandefinition了,我们知道Spring是一个IOC的容器,那么我们如何将这个一个个beandefinition放入我们的容器呢?换而言之,如何进行注册呢?相信看完本文后你会豁然开朗


    我们要分析的核心代码就是org.springframework.beans.factory.support.DefaultListableBeanFactory的registerBeanDefinition方法,代码如下:

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException {
    
        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                // 这里会对MethodOverrides做检查
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                                       "Validation of bean definition failed", ex);
            }
        }
    	// 判断是否有同名的bean被注册过了
        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        if (existingDefinition != null) {
            // 是否允许直接进行负载
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }
            else if (existingDefinition.getRole() < beanDefinition.getRole()) {
    		// ....省略部分代码,都是输出日志的
                
            // beanName做key放入map中,完成注册    
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            /**
             *这段代码比较难理解
             *我会在下面进行一一分析
             */
            if (hasBeanCreationStarted()) {
                synchronized (this.beanDefinitionMap) {
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }
    
        // 需要重置BeanDefinition,
        // 当前注册的bean的定义已经在beanDefinitionMap缓存中存在,
        // 或者其实例已经存在于单例bean的缓存中	
        if (existingDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }
    
    beanDefinitionNames,manualSingletonNamesbeanDefinitionMapfrozenBeanDefinitionNames是做什么的?
    1. beanDefinitionNames:按照注册顺序的beanDefinition名称集合

    2. beanDefinitionMap:beanDefinition的一个缓存,key是beanDefinitionName

    3. manualSingletonNames:手动注册的单例类名称,按照注册顺序排序

    4. frozenBeanDefinitionNames:在完成所以示例的初始化,Spring会将配置冻结起来,代表能缓存所有的bean的元数据,frozenBeanDefinitionNames保存的其实就是beanDefinitionNames,只不过是以数组形式保存的,用数组的原因是因为frozenBeanDefinitionNames在期望情况下不应该发生改变

    hasBeanCreationStarted(),这是做什么的?为什么这种情况下需要加锁?

    ​ 这里主要是判断是不是已经有bean被创建了,请注意,正常来说到目前为止,我们并有创建任何的bean,到现在我们做的仅仅是解析配置,并注册beanDefinition,如果有bean被创建意味着什么呢?大家可以思考下,当我们已经创建bean,代表着我们已经开始利用容器去进行我们的业务操作了,类似于我们执行了如下代码:

    public static void main(String[] args) {
      1.  ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
      2.  Object d1 = context.getBean("d2");
      3.   /** 利用获取的d1进行业务操作*/
      4.  DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
          factory.registerBeanDefinition("我要再注册一个bean",new GenericBeanDefinition());
    }
    

    在这种情况下,Spring容器无法保证使用者是在线程安全的情况下调用了,也就是无法保证下面的代码不会出现线程安全问题,所以需要加锁。

    this.beanDefinitionMap.put(beanName, beanDefinition);
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
    

    另外,我们需要注意的是,虽然beanDefinitionMap底层使用的是ConcurrentHashMap,但是需要注意的是,ConcurrentHashMap只能保证自身操作的线程安全,并不能保证整体业务的线程安全,大家不要产生误解!

    为什么要将this.beanDefinitionMap.put(beanName, beanDefinition)也放入同步代码块中?

    我们之前已经知道了beanDefinitionNames是要按照注册顺序保存的,所以必须要加放入的动作也进行同步,不然可能出现顺序错误

    为什么在加锁后给集合中添加元素还要进行一次类似于复制的操作(addAll)?

    我们在业务操作过程中,很可能调用Spring的某些方法,这些方法需要遍历beanDefinitionNames,这些遍历方法通常都是使用迭代器的,我们知道迭代过程中如果我们又对集合进行了添加,移除的操作,会引发快速失败机制如果对快速失败机制不熟悉的请自行百度。为了避免这种情况所以选择新建一个集合然后进行复制

  • 相关阅读:
    记第一场省选
    POJ 2083 Fractal 分形
    CodeForces 605A Sorting Railway Cars 思维
    FZU 1896 神奇的魔法数 dp
    FZU 1893 内存管理 模拟
    FZU 1894 志愿者选拔 单调队列
    FZU 1920 Left Mouse Button 简单搜索
    FZU 2086 餐厅点餐
    poj 2299 Ultra-QuickSort 逆序对模版题
    COMP9313 week4a MapReduce
  • 原文地址:https://www.cnblogs.com/daimzh/p/12854414.html
Copyright © 2011-2022 走看看