【Spring源码--IOC容器的实现】(四)BeanDefinition的注册
2016-08-18 20:14
831 查看
前言
本篇是IOC容器启动的最后一遍,也就是注册。但并不是DI(依赖注入)已经完成,这里只是完成对xml文件的解析、IOC容器的启动,具体的依赖注入需要getBean的时候完成。但是也有一个例外:那就是通过控制lazy-init属性来让容器实现对bean的预实例化。这个后面我们在讲。当代码读到这里,可以再针对前面的代码有目的的去回顾一下,先走通主线,再根据注释尽量去理解一些周边方法。
BeanDefinition在IOC容器注册
首先我们要回忆两点:1、发起注册的地方;2、注册的实现类。我们先来看第一点,上一篇博客前半部分我们讲了Bean的解析,在DefaultBeanDefinitionDocumentReader类的processBeanDefinition方法中,其中一个是去完成BeanDefinition的载入,另一个是完成注册。我们先来看下简略代码:
代码1.1:DefaultBeanDefinitionDocumentReader类的processBeanDefinition方法
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析方法
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//注册方法
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
现在我们找到了BeanDefinition的注册入口,我们知道BeanDefinitionHolder=BeanDefinition+beanName,所以这里数据我们是有的,来看下registerBeanDefinition的代码:
代码1.2:BeanDefinitionReaderUtils类的registerBeanDefinition方法
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 根据beanName注册Beandefinition.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 如果有别名,注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}
这里我们需要回忆第二点了,因为当我们找这个registerBeanDefinition的实现类的时候,会发现有三个实现类,那么具体取哪个?还记得我们最初容器建立的时候用的是什么容器么?简单回顾下代码:
代码1.3:AbstractRefreshableApplicationContext类的refreshBeanFactory方法
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建Factory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
而且,我们看下这个DefaultListFactory实现了BeanDefinitionRegistry接口,这个接口就是定义注册的。并且这个类中有这么一个HashMap,这里就是我们存放BeanDefinition的地方。
/** Map of bean definition objects, keyed by bean name */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
那么我们继续来看代码:
代码1.4:DefaultListableBeanFactory类的registerBeanDefinition方法
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");
//校验解析的BeanDefiniton
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) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
//检查是否有同名的BeanDefinition已经在IoC容器中注册,如果已经注册,
//并且不允许覆盖已注册的Bean,则抛出注册失败异常
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 {//如果允许覆盖,则同名的Bean,后注册的覆盖先注册的
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
else {
//IoC容器中没有已经注册同名的Bean,按正常注册流程注册
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
this.beanDefinitionMap.put(beanName, beanDefinition);
//重置所有已经注册过的BeanDefinition的缓存
resetBeanDefinition(beanName);
}
}
完了,这就结束了。到这里,代码就完成了IOC容器的初始化过程。此时,在使用的IOC容器DefaultListableBeanFactory中已经建立了整个Bean的配置信息,而且这些BeanDefinition已经可以被容器使用了,他们都在beanDefinitionMap中北检索和使用。容器的作用就是对这些信息进行处理和维护。这些信息室容器建立依赖反转的基础,有了这些基础数据,就可以完成最后的依赖注入了。
相关文章推荐
- 【Spring源码--IOC容器的实现】(三)BeanDefinition的载入和解析【I】
- 【Spring源码--IOC容器的实现】(三)BeanDefinition的载入和解析【II】
- Spring源码解析-BeanDefinition在IOC容器中的注册(三)
- 【Spring源码--IOC容器的实现】(二)BeanDefinition的Resource定位
- Spring源码阅读之IoC容器初始化3 -- BeanDefinition在IoC容器中的注册
- Spring源码阅读--BeanDefinition 在 IOC 容器中的注册
- (spring-第8回【IoC基础篇】)BeanDefinition在IoC容器中的注册
- spring技术内幕笔记:IoC容器的初始化过程(3)- BeanDefinition的注册
- spring 技术内幕--IOC初始化之BeanDefinition的在IOC容器中的注册
- Ioc容器beanDefinition-Spring 源码系列(1)
- Spring源码阅读之IoC容器初始化2 -- BeanDefinition载入与解析
- Spring技术内幕之IOC容器的实现(02)-BeanDefinition的Resource定位
- Spring源码分析----IOC容器的实现(IoC容器的初始化过程(定位、载入解析、注册))
- Spring IOC容器分析(2) -- BeanDefinition
- Spring源码分析-BeanDefinition加载、解析和注册
- 【Spring Framework 深入】—— IoC容器初始化 -> BeanDefinition的注册
- 7-spring源码3.2.18解读+spring技术内幕(关于BeanDefinition的注册)
- spring技术内幕笔记:IoC容器初始化过程(2)- BeanDefinition的载入
- Spring源码之旅(3)_BeanDefinition的解析与注册
- spring IOC源码学习(二):BeanDefinition资源加载