ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Spring源码阅读 之 bean的注册

2019-07-28 15:05:24  阅读:691  来源: 互联网

标签:manualSingletonNames Spring beanName beanDefinitionMap bean 源码 beanDefinitionNam


在前面我们已经学习了配置的加载,读取,解析。现在我们已经能够将一份配置转变成对应的一个个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,这些遍历方法通常都是使用迭代器的,我们知道迭代过程中如果我们又对集合进行了添加,移除的操作,会引发快速失败机制如果对快速失败机制不熟悉的请自行百度。为了避免这种情况所以选择新建一个集合然后进行复制

标签:manualSingletonNames,Spring,beanName,beanDefinitionMap,bean,源码,beanDefinitionNam
来源: https://blog.csdn.net/qq_41907991/article/details/97614337

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有