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. C#中显/隐式实现接口及其访问方法

    原贴地址: http://www.cnblogs.com/dudu837/archive/2009/12/07/1618663.html 在实现接口的时候,VS提供了两个菜单,一个是"实现接 ...

  2. 基于asp.net的ajax分页

    直接贴代码: <html> <head> <meta http-equiv="Content-Type" content="text/htm ...

  3. ALTER---删除字段

    ALTER TABLE table_name DROP (column1,column2,...); 例: ALTER TABLE userinfo DROP (name,num); 说明: 1.or ...

  4. Swing圆角边框的实现

    Swing圆角边框的实现方法: package com.justplay.basic; import java.awt.Color; import java.awt.Component; import ...

  5. 229. Majority Element II My Submissions Question

    Total Accepted: 23103 Total Submissions: 91679 Difficulty: Medium Given an integer array of size n, ...

  6. ios 多线程-GCD-NSOperation

    一.线程间的通讯 1.使用NSObject类的方法performSelectorInBackground:withObject:来创建一个线程. 具体的代码:隐式创建,自动启动 [Object per ...

  7. using System.Collections.Generic;

    public class CommonClass { public static void ShowInt(int iValue) { //typeof(CommonClass) typeof关键字 ...

  8. SQL通过日期计算年龄

    首先建立一个表如下: ======================= BirthDay datetime not null Age 通过公式计算得出 ======================= 以 ...

  9. 关于delete

    上面三图在debug下,delete的时候会以fe ee覆盖指针所指向要回收内存前后较大一块区域的值. 下图对于release下delete的时候会先用fe ee覆盖指针所指位置起6*4=24字节, ...

  10. 利用多核来加速Linux命令行

    本文转载自 多核CPU来加速 awk, sed, bzip2, grep, wc等,如需查看原文,请点此链接进入. -------------------------------我是分割线 开始 -- ...