本文要看啥


这个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 两篇文章的坑还留着呢,继续…