1.0 registerBeanDefinition

  对于配置文件,解析也解析完了,装饰也装饰完了,对于得到的BeanDefinition已经可以满足后续的使用了,唯一剩下的工作就是注册了,

也就是: processBeanDefinition 方法中的

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

  代码如下:

 /**
* Process the given bean element, parsing the bean definition and registering it with
* the registry.
*/
protected void processBeanDefinition(Element ele,
BeanDefinitionParserDelegate delegate) {
// 委托BeanDefinition类的parseBeanDefinitionElement方法进行元素解析,返回Beandefinition
// 类型的实例bdHolder 经过这个方法之后,
// bdHolder实例已经包含了我们配置文件中的各种属性了,例如 : class,name,id,alias
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//当返回的bdHolder 不为空的情况下,若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析.
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 解析完成之后,需要对解析后的bdHolder 进行注册,同样注册操作委托给了BeanDefinitionUtils 的 registerBeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to register bean definition with name '"
+ bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 最后发出响应事件,通知相关的监听器,这个bean已经加载完了.
getReaderContext().fireComponentRegistered(
new BeanComponentDefinition(bdHolder));
}
}

  我们继续追踪下去:

 /**
* Register the given bean definition with the given bean factory.
*
* @param definitionHolder the bean definition including name and aliases
* @param registry the bean factory to register with
* @throws BeanDefinitionStoreException if registration failed
*/
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder,
BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name.
// 使用 beanName 作为唯一标示注册
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 注册所有别名
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}

  从上面代码可以看出,解析的BeanDefinition都会被注册到BeanDefinitionRegister 类型的实例 register 中,而对于beanDefinition 的注册分为2部分,

1. 通过beanName 注册

2. 通过别名注册;

2.0 通过beanName 注册

  对于使用beanDefinition的注册,或许很多人都认为是将BeanDefinition 直接放入map中就好了,使用beanName作为Key ,实际上,的确Spring的确是这么做了,只不过除此之外,它还做了点别的事情,

我们来看看代码:

 @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 {/*
* 注册前最后一次效验,这里的效验不同之前的XML文件效验,主要是对于AbstractBeanDefinition
* 属性中的methodOverrides 效验; 效验methodOverrides 是否与工厂方法并存或者methodOverrides
* 对应的方法根本不存在
*/
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(
beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 应为beanDefinitionMap 是全局变量,这里定会存在并发
synchronized (this.beanDefinitionMap) {
// 处理注册已经注册的beanName情况
BeanDefinition oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
// 如果对应beanName已经注册 并且在配置文件中配置了beanName不能被覆盖,则抛出异常
if (!this.allowBeanDefinitionOverriding) {
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 (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '"
+ beanName + "': replacing [" + oldBeanDefinition
+ "] with [" + beanDefinition + "]");
}
}
}
else {
// 记录beanName
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
// 注册beanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}
// 重置所有beanName 对应的缓存(清楚之前对应beanName 的缓存)
resetBeanDefinition(beanName);
}

  上面代码中,我们看到对于bean在处理方式上主要做了几个步骤,

  1. 注册前最后一次效验,这里的效验不同之前的XML文件效验,对于AbstractBeanDefinition属性中的methodOverrides 效验; 效验methodOverrides 是否与工厂方法并存或者methodOverrides

  2. 如果对应beanName已经注册 并且在配置文件中配置了beanName不能被覆盖,则抛出异常  3. 加入map缓存

  4.清除解析之前对应beanName 的缓存

2. 通过别名注册BeanDefinition

  在理解注册beanName的原理后,理解别名注册就容易多了;

代码如下:

SimpleAliasRegistry.java

 @Override
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
// 如果beanName与alias 相同的话,不记录alias ,并删除对应的alias
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
// 如果alias 不允许被覆盖则抛出异常
if (!allowAliasOverriding()) {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null && !registeredName.equals(name)) {
throw new IllegalStateException("Cannot register alias '" + alias
+ "' for name '" + name
+ "': It is already registered for name '" + registeredName
+ "'.");
}
}
// alias 循环检查, 当 A -> B 存在时, A->C->B 的时候则会抛出异常
checkForAliasCircle(name, alias);
// 注册alias
this.aliasMap.put(alias, name);
}
}

  经过一番波折,  最后通过getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));发出响应事件,通知相关的监听器,这个bean已经加载完了. 这里的实现只为了扩展,当程序开发员需要对注册BeanDefinition事件进行监听的时候可以通过注册监听器的方式将处理逻辑写入监听器中,目前,Spring 并没有对此事件做任何逻辑处理.

  

4.0 spring-注册解析的Bean的更多相关文章

  1. Spring 常用配置、Bean

    spring模块 Spring-Core: Core包是框架的最基础部分,并提供依赖注入(Dependency Injection)管理Bean容器功能. Spring-Context:(Spring ...

  2. Spring Bean注册解析(二)

           在上文Spring Bean注册解析(一)中,我们讲解了Spring在注册Bean之前进行了哪些前期工作,以及Spring是如何存储注册的Bean的,并且详细介绍了Spring是如何解析 ...

  3. 手撸Spring框架,设计与实现资源加载器,从Spring.xml解析和注册Bean对象

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你写的代码,能接的住产品加需求吗? 接,是能接的,接几次也行,哪怕就一个类一片的 i ...

  4. spring概念简介、bean扫描与注册实现方式

    写在前面:本文作为整理,包含很多个人理解,有跳跃成份,初学者如果看晕了,可以先看其它同类文章,或者……多看几遍. 一.概念部分: 1.spring概念:网上有很多 2.spring核心:IOC(DI) ...

  5. spring中IOC容器注册和获取bean的实例

    spring中常用的功能主要的是ioc和aop,此处主要说明下,实例注册和使用的方法,此为学习后的笔记记录总结 1.使用xml文件配置 在idea中创建maven工程,然后创建实例Person,然后在 ...

  6. Spring 通过@Import实现Bean的注册

    今天看到一个神奇的用法, Spring可以通过@Import导入实现了ImportBeanDefinitionRegistrar接口的类来注册那个类. ImportBeanDefinitionRegi ...

  7. 使用Spring容器动态注册和获取Bean

    有时候需要在运行时动态注册Bean到Spring容器,并根据名称获取注册的Bean.比如我们自己的SAAS架构的系统需要调用ThingsBoard API和Thingsboard交互,就可以通过Thi ...

  8. Spring配置文件解析--bean属性

    1.bean设置别名,多个别名用逗号隔开 <!--使用alias--> <bean id="app:dataSource" class="...&quo ...

  9. 品Spring:SpringBoot发起bean定义注册的“二次攻坚战”

    上一篇文章整体非常轻松,因为在容器启动前,只注册了一个bean定义,就是SpringBoot的主类. OK,今天接着从容器的启动入手,找出剩余所有的bean定义的注册过程. 具体细节肯定会颇为复杂,同 ...

随机推荐

  1. 使用CSS实现一个简单的幻灯片效果

    方法一: 简单的CSS代码实现幻灯片效果 方法二: 使用CSS3 Animation来制作幻灯片 方法一: 简单的CSS代码实现幻灯片效果 话不多说,直接上代码 <!DOCTYPE html&g ...

  2. css3 选择器记

    css3 选择器 根据所获取页面中元素的不同,把css3选择器分为五大类: 基本选择器 层次选择器 伪类选择器 动态伪类选择器 目标伪类选择器 语言伪类选择器 UI元素状态伪类选择器 结构伪类选择器 ...

  3. SQL数据库设计三范式

    关系型数据库将数据库设计需要遵循的一些规则叫做“范式”,最基本的三个范式(1NF.2NF.3NF)简称三范式.第一范式是满足第二范式的基础,而第一.二范式又是满足第三范式的基础. 第一范式 表中的字段 ...

  4. 每天一道LeetCode--118. Pascal's Triangle(杨辉三角)

    Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5,Retur ...

  5. Ssqlserver 关于Grouping sets

    sqlserver2008之后引入Grouping sets是group by的增强版本,Grouping sets 在遇到多个条件时,聚合是一次性从数据库中取出所有需要操作的数据,在内存中对数据库进 ...

  6. 更换用installshield打包生成exe文件的图标【转】

    最近在研究用installshield2010为自己做的产品打包,自己在网上找写资料,胡乱折腾,最后弄成了一个exe安装包,想要修改exe文件的图标,发现Basic MSI project 无法用in ...

  7. Javascript之计时

    // <!DOCTYPE html> <meta http-equiv="Content-Type" content="text/html; chars ...

  8. HOWTO: Be more productive

    ---by   Aaron Swartz HOWTO: Be more productive “With all the time you spend watching TV,” he tells m ...

  9. C#事件解析

    事件(event),这个词儿对于初学者来说,往往总是显得有些神秘,不易弄懂.而这些东西却往往又是编程中常用且非常重要的东西.大家都知道windows消息处理机制的重要,其实C#事件就是基于window ...

  10. Git 技巧小结

    本篇博客内的内容,主要摘抄自 廖雪峰的 Git教程,这篇教程写的通俗易懂,步步深入,是我见过最棒的Git教程了.下面的全部内容,摘抄自此教程,有需要的朋友,请看完整版. Git版本库 git在创建版本 ...