Spring源码笔记-1.4 获取bean流程之注册BeanDefinition

# 本文要看啥


这个bean总算是解析完了,也装饰完了,等于是说信息我们都提取好了,现在该做的就是去注册啦.

1
 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());

就是这个方法了

# 开始进入方法吧


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
    String beanName = definitionHolder.getBeanName();
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        String[] var4 = aliases;
        int var5 = aliases.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String alias = var4[var6];
            registry.registerAlias(beanName, alias);
        }
    }
}
  1. 拿到beanName

  2. 用beanName和bd去registry里注册.

    registry是个接口,具体的实现在4.3.7版本中看到两种,一种是SimpleBeanDefinitionRegistry里,和它名字一样非常简单,就是塞进map, 另外一种就复杂了,稍后我们再来看

  3. 用beanName和alias去registry里注册

    注册aliases也是接口 ,BeanDefinitionRegistry还是继承自AliasRegistry的, 4.3.7只看到SimpleAliasRegistry一种实现

    注册alias就简单多了, SimpleAliasRegistry里有

    1
    
      private final Map<String, String> aliasMap = new ConcurrentHashMap(16);
    

    稍后我再另开一文把SimpleAliasRegistry来读一遍

# 注册BeanDefinitionHolder


除了之前的SimpleBeanDefinitionRegistry 直接往map里面塞值的注册方式之外,我们还有DefaultListableBeanFactory 这个类来实现注册功能,我们主要来了解一下这种注册方式

  1. 校验空参数

  2. 如果bdh是AbstractBeanDefinition ,那就要执行它的validate() 校验

    1. bdh的methodOverride和它的工厂不能同时存在
    2. 如果它beanClass有值,那就要为方法覆盖做准备 prepareMethodOverrides() ,遍历它的overrides,找这个Bean的Class里面有几个叫这个名字的方法, 如果0个,就抛错,如果1个,就设置重载属性为false表示没重载.
  3. 从这个工厂里面beanDefinitionMap 拿原来的叫这个beanName的bd出来,如果不为空,进行下列校验及操作

    书中源码spring 3.2 采用sychronized , 而4.3版本的beanDefinitionMap已经用上了ConcurrentHashMap,并且map的value用了BD,而不是3.2里面的Object

    1. 如果工厂不允许bean覆盖,那么抛错
    2. 如果旧bean的role值小于新的,或者新旧bd完全相等,或者不相等,各打印一下日志
    3. 把新的bd放入beanDefinitionMap
  4. 第一次注册这个bean, 如果本工厂的alreadyCreated 也是空的话,那就不用加锁,直接

    1
    2
    3
    4
    5
    6
    
     //map里塞入bd                
    this.beanDefinitionMap.put(beanName, beanDefinition);
     //已注册名字里加入beanName              
    this.beanDefinitionNames.add(beanName);
     //唯一名册里移出beanName(啥时候加入了???)              
    this.manualSingletonNames.remove(beanName);
    

    如果本工程已经有创建过bean了,那么接下来一段代码就要加synchronized了

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    Map var4 = this.beanDefinitionMap;
    synchronized(this.beanDefinitionMap) {
       //map里塞入bd        
        this.beanDefinitionMap.put(beanName, beanDefinition);
      //注意到后面这段操作其实是新建个数组,加上这次注册beanName,然后替换之前的数组....
      //为什么这么复杂呢???
        List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);
        updatedDefinitions.addAll(this.beanDefinitionNames);
        updatedDefinitions.add(beanName);
        this.beanDefinitionNames = updatedDefinitions;
      //如果唯一名册里包含beanName
        if (this.manualSingletonNames.contains(beanName)) {
          //就移出去,但是这里也和上面一样,搞替换....
            Set<String> updatedSingletons = new LinkedHashSet(this.manualSingletonNames);
            updatedSingletons.remove(beanName);
            this.manualSingletonNames = updatedSingletons;
        }
    }
    

    疑问: 这里都用了替换,明明beanDefinitionNamesmanualSingletonNames 都是volatile声明的,每次读都从主内存取,每次写都都将值写到主内存, 这里为什么还要这么麻烦得去开新的list和set,替换旧的呢?

    答:从网上搜到,volatile修饰的变量如果是对象或数组之类的,其含义是对象或数组的地址具有可见性,但是数组或对象内部的成员改变不具备可见性,那等于就是说非得整个地址替换才行咯.

    很爽,从这本书要跳到我正在读的并发编程相关的内容了,这几天再去研究一波volatile,可以再开一文. 准备参考地址

然后this.frozenBeanDefinitionNames = null; 这个冻结/取消冻结bdNames的功能暂时也不知道干啥的,在注册和删除bd的地方看到了这样的置为null,

​ 只在freezeConfiguration() 方法内看到把它置为bdNames转的String[] .

​ 另外在getBeanDefinitionNames() 方法里有判断这个

然后

1
2
3
4
5
//如果有老bd或者 这个bean是单例对象?
if (oldBeanDefinition != null || this.containsSingleton(beanName)) {
  //重置bean
            this.resetBeanDefinition(beanName);
        }

重置的主要内容有

  • 清除mergedBD
  • 销毁单例
  • 遍历之前的bean,看新注册的这个bean是谁的爹(parent),那些儿子也都要调用这个方法进行重置

不得不说这个DefaultListableBeanFactory 的成员变量实在太多了,搞不清楚一个个都是要干啥的,后面再次遇到的时候再慢慢看吧

# 注册Alias


4.3.7版本的实现在SimpleAliasRegistry 里, 详情见 这一篇博文

# 注册完成


其实没有操作了, 但是作者留了个位置在这

1
  this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

如果程序开发人员需要对注册BeanDefinition时间进行监听时,可以通过注册监听器的方式来做.Spring并没有再此做任何逻辑处理

# 告一段落


至此,我们的解析注册Bean标签可算是告一段落啦,虽然里面还留下了很多疑问待后续阅读中弄清.

先想想之前是在哪分叉出去的吧.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, "import")) {
            this.importBeanDefinitionResource(ele);
        } else if (delegate.nodeNameEquals(ele, "alias")) {
            this.processAliasRegistration(ele);
        } else if (delegate.nodeNameEquals(ele, "bean")) {
          //就是这里啦,我们先去阅读最复杂的bean的
            this.processBeanDefinition(ele, delegate);
        } else if (delegate.nodeNameEquals(ele, "beans")) {
          //这个地方我还记得,beans标签是绕回去解析的,不用看了
            this.doRegisterBeanDefinitions(ele);
        }
    }

再之前呢,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();

            for(int i = 0; i < nl.getLength(); ++i) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element)node;
                    if (delegate.isDefaultNamespace(ele)) {
                      //就是这个地方,差不多想起来了,beans标签里面的子元素, 如果是默认命名空间,就按默认方式去解析
                        this.parseDefaultElement(ele, delegate);
                    } else {
                      //这里是个分叉,解析自定义元素,以后再看
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        } else {
    		//这和上面一样的分叉,解析自定义元素
            delegate.parseCustomElement(root);
        }

    }

再之前呢

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
protected void doRegisterBeanDefinitions(Element root) {
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
    if (this.delegate.isDefaultNamespace(root)) {
        String profileSpec = root.getAttribute("profile");
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
            if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                }

                return;
            }
        }
    }

    this.preProcessXml(root);
    //从这里走出去的
    this.parseBeanDefinitions(root, this.delegate);
    this.postProcessXml(root);
    this.delegate = parent;
}

到这还要温习一遍的话,可以去看Spring的第一篇笔记

哈哈,这么点东西,读了一个礼拜,读了后面的忘了前面的,还是在读读记记的情况下,

要是没有这个笔记,这时候估计已经迷路得要放弃了,不容易啊

不说了 SimpleAliasRegistryvolatile 两篇文章的坑还留着呢,继续…