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,这些遍历方法通常都是使用迭代器的,我们知道迭代过程中如果我们又对集合进行了添加,移除的操作,会引发快速失败机制如果对快速失败机制不熟悉的请自行百度。为了避免这种情况所以选择新建一个集合然后进行复制

  • 相关阅读:
    MOSS 2010 修改管理员密码 欧阳锋
    MOSS2010 中“找不到位于xxxx的web应用程序”的解决办法 欧阳锋
    MSSQL 2008 无法修改表问题的解决 欧阳锋
    不是每个在你身上拉屎的都是你的敌人 欧阳锋
    笑一笑 欧阳锋
    隐藏MOSS2010 左边的导航 欧阳锋
    爱情与婚姻的区别 欧阳锋
    两年后,我们怎么办
    C#控件的闪烁问题解决方法总结
    Linux内核编译配置过程
  • 原文地址:https://www.cnblogs.com/daimzh/p/12854414.html
Copyright © 2011-2022 走看看