注册解析的BeanDefinition
Posted wcj-java
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了注册解析的BeanDefinition相关的知识,希望对你有一定的参考价值。
注册解析的BeanDefinition
配置文件解析完了,对于得到的beanDefinition已经可以满足后续的使用要求了,唯一还剩下的工作就是注册了,也就是processBeanDefinition函数中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())代码的解析了。
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // 使用beanName做唯一标识注册. String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 注册所有别名. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
从上面的代码可以看出,解析的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,而对于beanDefinition的注册分成了两部分:通过beanName的注册以及通过别名的注册。
1通过beanName注册BeanDefinition
对于beanDefinition的注册,你可能会觉得将beanDefinition直接放入map中就好了,使用beanName作为key。确实,Spring就是这么做的,只不过除此之外,它还做了别的事情。
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 { // 注册前的最后一次校验,这里的校验不同于之前的xml文件校验,主要对于AbstractBeanDefinition属性中的methodOverrides校验 // 校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在。 ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } BeanDefinition oldBeanDefinition; oldBeanDefinition = this.beanDefinitionMap.get(beanName); // 处理注册已经注册的beanName情况 if (oldBeanDefinition != null) { // 如果对应的BeanName已经注册了且在配置中配置了bean不允许被覆盖,则抛出异常。 if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean ‘" + beanName + "‘: There is already [" + oldBeanDefinition + "] bound."); } else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (this.logger.isWarnEnabled()) { this.logger.warn("Overriding user-defined bean definition for bean ‘" + beanName + "‘ with a framework-generated bean definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(oldBeanDefinition)) { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean ‘" + beanName + "‘ with a different definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Overriding bean definition for bean ‘" + beanName + "‘ with an equivalent definition: replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } this.beanDefinitionMap.put(beanName, beanDefinition); } else { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) // 因为beanDefinitionMap是全局变量,这里定会存在并发访问的情况。 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 { // Still in startup registration phase // 注册beanDefinition this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if (oldBeanDefinition != null || containsSingleton(beanName)) { // 重置所有beanName对应的缓存 resetBeanDefinition(beanName); } }
上面的代码中我们看到,在对于bean的注册处理方式上,主要进行了几个步骤。
(1) 对AbstractBeanDefinition的校验。在解析XML文件的时候我们提过校验,但是此校验非彼校验,之前的校验是针对XML格式的校验,而此时的校验是针对于AbstractBeanDefinition的methodOverrides属性的。
(2) 对beanName已经注册的情况的处理,如果设置了不允许bean的覆盖,则需要抛出异常,否则直接覆盖。
(3) 加入map缓存。
(4) 清楚解析之前留下的对应beanName的缓存。
2.通过别名注册BeanDefinition
在理解了注册bean的原理后,理解注册别名的原理就容易多了。
public void registerAlias(String name, String alias) { Assert.hasText(name, "‘name‘ must not be empty"); Assert.hasText(alias, "‘alias‘ must not be empty"); synchronized (this.aliasMap) { // 如果beanName与alias相同的话不记录alias,并删除对应的alias。 if (alias.equals(name)) { this.aliasMap.remove(alias); } else { String registeredName = this.aliasMap.get(alias); if (registeredName != null) { if (registeredName.equals(name)) { // An existing alias - no need to re-register return; } // 如果alias不允许被覆盖则抛出异常。 if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot register alias ‘" + alias + "‘ for name ‘" + name + "‘: It is already registered for name ‘" + registeredName + "‘."); } } //当A->B存在时,若再次出现A->C->B时候则会抛出异常 checkForAliasCircle(name, alias); this.aliasMap.put(alias, name); } } }
由以上代码中可以得知注册alias的步骤如下:
(1) alias与beanName相同情况处理。若alias与beanName名称相同则不需要处理并删除掉原有alias。
(2) alias覆盖处理。若aliasName已经使用并已经指向了另一beanName则需要用户的设置进行处理。
(3) alias循环检查。当A->B存在时,若再次出现A->C->B时候则会抛出异常。
(4) 注册alias。
以上是关于注册解析的BeanDefinition的主要内容,如果未能解决你的问题,请参考以下文章
springIOC源码解析之BeanDefinition的注册
[死磕 Spring 14/43] --- IOC 之注册解析的 BeanDefinition