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. CSS3秘笈第三版涵盖HTML5学习笔记13~17章

    第13章,构建基于浮动的布局 使用的是float(浮动)属性 注:float:none值将取消所有浮动,通常只用来取消元素中已经应用的浮动. 切记:不需要给正文的div设计宽度,即使设计成固定宽度也不 ...

  2. Jersey(1.19.1) - Building URIs

    A very important aspects of REST is hyperlinks, URIs, in representations that clients can use to tra ...

  3. Git CMD - clone: Clone a repository into a new directory

    命令格式 git clone [--template=<template_directory>]  [-l] [-s] [--no-hardlinks] [-q] [-n] [--bare ...

  4. 和阿文一起学H5——音乐素材

    国内 1.网易云音乐 http://music.163.com/ 网易云音乐有听歌识曲功能,听几秒中可听出是什么歌. 微信摇一摇也有听歌识曲功能. 2.只要伴奏不要人声 http://5sing.ku ...

  5. Linux命令(5):cp命令

    1.作用: 将给出的文件或目录复制到另一文件或目录中: 2.格式: cp  [选项] 源文件或目录 目标文件或目录. 3.常见参数: 4.使用实例: [root@localhost ~]# cp -a ...

  6. PHP获取本周第一天和最后一天

    //本周的第一天和最后一天  代码如下 复制代码 $date=new DateTime();$date->modify('this week');$first_day_of_week=$date ...

  7. 动态图片加到UIImageView中

    //1.添加一个.gif类型的动态的图片,用到URLForResource方法,gif是图片的格式,FlagZombie是图片的名字 @implementation ViewController- ( ...

  8. dorado7 重装了tomcat后配置路径

    在Windows->Preferences->Server->Runtime Environments把先前的工程Servers删除掉

  9. 暑假集训(5)第一弹——— Super Jumping! Jumping! Jumping!(hdu1087)

    题意概括:在上次与娑殚的三次博弈中,你们都取得了胜利.便向娑殚提出要求,借助他的力量,传送到一个安全的地方. 你们的愿望达成了,不过,你和小A似乎失散了. 街上人来人往的特别热闹,每一个人的脸上都洋溢 ...

  10. 创建ID3D11Device可能会遇到的问题,不能使用具体的IDXGIAdapter

    要使用具体硬件的显示适配器创建D3D11必须把driverTypes设为D3D_DRIVER_TYPE_UNKNOWN 如下 // 创建D3D11设备 HRESULT hr = D3D11Create ...