您的位置:首页 > 编程语言 > Java开发

Spring源码阅读之IoC容器初始化3 -- BeanDefinition在IoC容器中的注册

2013-04-18 16:46 1136 查看
注1:Spring源码基于Spring3.1版本

注2:参考《Spring技术内幕》第二版

前面分析了IoC容器初始化过程中的Resource资源定位和BeanDefinition的载入与解析两个步骤,现在再分析一下在完成BeanDefinition的载入与解析后,Spring是如何将解析所得的BeanDefinition向IoC容器注册的。

在BeanDefinition载入与解析完成之后,BeanDefinition信息已经在IoC容器内部建立起了自己的数据结构,但这些数据还不能供IoC容器直接使用,需要在IoC容器中对这些BeanDefinition数据进行注册。在DefaultListableBeanFactory中,是通过一个HashMap类型的成员变量beanDefinitionMap 来持有载入的BeanDefinition的,在DefaultListableBeanFactory的源码部分可以看到:

/** List of bean definition names, in registration order */
private final List beanDefinitionNames = new ArrayList();

/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

将解析得到的BeanDefinition向IoC容器中的beanDefinitionMap注册的过程是在载入BeanDefinition完成后进行的。而前面的分析注重的是BeanDefinition的载入与解析,没有分析到BeanDefinition向IoC容器这一分支上来,看下面这段代码:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 具体的解析委托给BeanDefinitionParserDelegate来完成
// BeanDefinitionHolder是BeanDefinition的封装类, 封装了BeanDefinition、Bean的名字和别名, 用它来完成向IoC容器注册.
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 向IoC容器注册解析得到的BeanDefinition.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
}
// BeanDefinition注册完成后, 发送事件.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

在上面这段代码中,方法的第一句代码是调用BeanDefinitionParserDelegate的parseBeanDefinitionElement方法具体解析<bean>标签,返回的是BeanDefinitionHolder对象,该对象持有BeanDefinition、Bean的名字以及别名等信息,这是前面分析的重点;而接下来的代码便是向IoC容器注册BeanDefinition了,调用的是BeanDefinitionReaderUtils工具类中的registerBeanDefinition方法,将前面解析得到的BeanDefinitionHolder以及IoC容器对象(DefaultListableBeanFactory类实现了BeanDefinitionRegistry接口)作为参数传进方法中。

下面就跟着代码去看一下注册是如何发生的。

BeanDefinitionReaderUtils类中的registerBeanDefinition方法:

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder,
BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
String beanName = definitionHolder.getBeanName();
// 向IoC容器注册BeanDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

// 如果解析的BeanDefinition有别名, 向容器为其注册别名.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}

上面代码显示,其实真正完成BeanDefinition注册的是BeanDefinitionRegistry的registerBeanDefinition方法,而DefaultListableBeanFactory类实现了BeanDefinitionRegistry接口,所以真正完成BeanDefinition注册正是DefaultListableBeanFactory类本身。

DefaultListableBeanFactory源码:

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 对解析得到的BeanDefinition校验
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
} catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 注册的过程中需要线程同步, 以保证数据的一致性
synchronized (this.beanDefinitionMap) {
// 检查是否有同名的BeanDefinition已经在IoC容器中注册, 如果已经注册, 并且不允许覆盖已注册的Bean, 则抛出异常
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
} else {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
} else {
// 没有同名BeanDefinition注册过, 将Bean名字存入beanDefinitionNames
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
// 以BeanName为key, BeanDefinition为value存入beanDefinitionMap, 有同名BeanDefinition会覆盖.
this.beanDefinitionMap.put(beanName, beanDefinition);

resetBeanDefinition(beanName);
}
}

BeanDefinition向IoC容器的注册到这里就完成了。当完成了所有BeanDefinition的注册,IoC容器的初始化过程也就完成了。此时在IoC容器DefaultListableBeanFactory中已经建立了整个Bean的配置信息,而且这些BeanDefinition已经可以被容器使用了,它们都被维护在BeanDefinitionMap里。容器的作用就是对这些Bean信息进行维护和处理,这些信息是容器建立控制反转的基础。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Spring Spring源码 JAVA