找入口

AbstractRefreshableApplicationContext类的refreshBeanFactory方法中第13行代码:

protected final void refreshBeanFactory() throws BeansException {
// 如果之前有IoC容器,则销毁
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建IoC容器,也就是DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
// 加载BeanDefinition对象,并注册到IoC容器中(重点)
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

流程解析

  • 进入AbstractXmlApplicationContext的loadBeanDefinitions方法:

    • 创建一个XmlBeanDefinitionReader,通过阅读XML文件,真正完成BeanDefinition的加载和注册。
    • 配置XmlBeanDefinitionReader并进行初始化。
    • 委托给XmlBeanDefinitionReader去加载BeanDefinition。
 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 创建一个BeanDefinition阅读器,通过阅读XML文件,真正完成BeanDefinition的加载和注册
XmlBeanDefinitionReader beanDefinitionReader = new
XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
// 委托给BeanDefinition阅读器去加载BeanDefinition
loadBeanDefinitions(beanDefinitionReader);
} protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws
BeansException, IOException {
// 获取资源的定位
// 这里getConfigResources是一个空实现,真正实现是调用子类的获取资源定位的方法
// 比如:ClassPathXmlApplicationContext中进行了实现
// 而FileSystemXmlApplicationContext没有使用该方法
Resource[] configResources = getConfigResources();
if (configResources != null) {
// XML Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的资源
reader.loadBeanDefinitions(configResources);
}
// 如果子类中获取的资源定位为空,则获取FileSystemXmlApplicationContext构造方法中setConfigLocations方法设置的资源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// XML Bean读取器调用其父类AbstractBeanDefinitionReader读取定位的资源
reader.loadBeanDefinitions(configLocations);
}
}
  • loadBeanDefinitions方法经过一路的兜兜转转,最终来到了XmlBeanDefinitionReaderdoLoadBeanDefinitions方法:

    • 一个是对XML文件进行DOM解析;
    • 一个是完成BeanDefinition对象的加载与注册
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 通过DOM4J加载解析XML文件,最终形成Document对象
Document doc = doLoadDocument(inputSource, resource);
// 通过对Document对象的操作,完成BeanDefinition的加载和注册工作
return registerBeanDefinitions(doc, resource);
}
//省略一些catch语句
catch (Throwable ex) {
......
}
}
  • 此处我们暂不处理DOM4J加载解析XML的流程,我们重点分析BeanDefinition的加载注册流程
  • 进入XmlBeanDefinitionReaderregisterBeanDefinitions方法:
    • 创建DefaultBeanDefinitionDocumentReader用来解析Document对象。
    • 获得容器中已注册的BeanDefinition数量
    • 委托给DefaultBeanDefinitionDocumentReader来完成BeanDefinition的加载、注册工作。
    • 统计新注册的BeanDefinition数量
public int registerBeanDefinitions(Document doc, Resource resource) throws
BeanDefinitionStoreException {
// 创建DefaultBeanDefinitionDocumentReader用来解析Document对象
BeanDefinitionDocumentReader documentReader =
createBeanDefinitionDocumentReader();
// 获得容器中注册的Bean数量
int countBefore = getRegistry().getBeanDefinitionCount();
//解析过程入口,BeanDefinitionDocumentReader只是个接口
//具体的实现过程在DefaultBeanDefinitionDocumentReader完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 统计注册的Bean数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
  • 进入DefaultBeanDefinitionDocumentReaderregisterBeanDefinitions方法:

    • 获得Document的根元素标签
    • 真正实现BeanDefinition解析和注册工作
 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext
{
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
// 获得Document的根元素<beans>标签
Element root = doc.getDocumentElement();
// 真正实现BeanDefinition解析和注册工作
doRegisterBeanDefinitions(root);
}
  • 进入DefaultBeanDefinitionDocumentReaderdoRegisterBeanDefinitions方法:

    • 这里使用了委托模式,将具体的BeanDefinition解析工作交给了BeanDefinitionParserDelegate去完成
    • 在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性
    • 委托给BeanDefinitionParserDelegate,从Document的根元素开始进行BeanDefinition的解析
    • 在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性
 protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one. // 这里使用了委托模式,将具体的BeanDefinition解析工作交给了BeanDefinitionParserDelegate去完成
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 在解析Bean定义之前,进行自定义的解析,增强解析过程的可扩展性
preProcessXml(root);
// 委托给BeanDefinitionParserDelegate,从Document的根元素开始进行BeanDefinition的解析
parseBeanDefinitions(root, this.delegate);
// 在解析Bean定义之后,进行自定义的解析,增加解析过程的可扩展性
postProcessXml(root); this.delegate = parent;
}

【死磕 Spring】—— IoC 之加载 BeanDefinition的更多相关文章

  1. 深入Spring之IOC之加载BeanDefinition

    本文主要分析 spring 中 BeanDefinition 的加载,对于其解析我们在后面的文章中专门分析. BeanDefinition 是属于 Spring Bean 模块的,它是对 spring ...

  2. Spring源码加载BeanDefinition过程

    本文主要讲解Spring加载xml配置文件的方式,跟踪加载BeanDefinition的全过程. 源码分析 源码的入口 ClassPathXmlApplicationContext构造函数 new C ...

  3. Dubbo死磕之扩展点加载ExetnsionLoader

    dubbo的SPI机制与JDK的SPI机制对比        dubbo一款阿里一款开源的RPC框架,他本身是一款非常复杂的系统,我们主要针对里边的一些核心点来展开分析,其中duboo里的一种核心机制 ...

  4. Spring源码:Spring IoC容器加载过程(2)

    Spring源码版本:4.3.23.RELEASE 一.加载XML配置 通过XML配置创建Spring,创建入口是使用org.springframework.context.support.Class ...

  5. Spring IOC bean加载过程

    首先我们不要在学习Spring的开始产生畏难情绪.Spring没有臆想的那么高深,相反,它帮我们再项目开发中制定项目框架,简化项目开发.它的主要功能是将项目开发中繁琐的过程流程化,模式化,使用户仅在固 ...

  6. Spring源码:Spring IoC容器加载过程(1)

    Spring源码版本:4.3.23.RELEASE 一.加载过程概览 Spring容器加载过程可以在org.springframework.context.support.AbstractApplic ...

  7. 梳理源码:spring ioc容器加载的流程图

  8. 【死磕 Spring】—— IoC 之 Spring 统一资源加载策略

    本文主要基于 Spring 5.0.6.RELEASE 摘要: 原创出处 http://svip.iocoder.cn/Spring/IoC-load-Resource/ 在学 Java SE 的时候 ...

  9. 【死磕 Spring】----- IOC 之 加载 Bean

    原文出自:http://cmsblogs.com 先看一段熟悉的代码: ClassPathResource resource = new ClassPathResource("bean.xm ...

随机推荐

  1. shell中括号的使用

    在这里我想说的是几种shell里的小括号,大括号结构和有括号的变量,命令的用法,如下: 1.${var} 2.$(cmd) 3.()和{} 4.${var:-string},${var:+string ...

  2. RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本━新增岗位管理-WinForm部分

    RDIFramework.NET ━ .NET快速信息化系统开发框架 V2.8 版本 新增岗位管理-WinForm部分 岗位(职位)管理模块主要是针对组织机构的岗位(职位)进行管理,包括:增加.修改. ...

  3. Centos7 Cacti-0.8.8g安装及SNMP简介

    在官网可以看到关于cacti的下载说明http://www.cacti.net/download_cacti.php Download Cacti The latest stable version ...

  4. location.reload()加载时有弹出框

    解决办法: 1.试试 window.location.href=window.location.href 2.当前页有被post数据的时候reload就会提示刷新 可以尝试重新定向页面地址 3.rel ...

  5. 绕过CDN查找网站真实IP方法

    查找网站 源IP方法: 如果遇到需要绕过CDN,查找网站真实IP地址时,可以采用如下方法: 假设主站服务和邮件服务在同一台服务器: 1.在网站用QQ邮箱注册账号: 2.收取注册验证邮件: 3.查看邮件 ...

  6. ArrayBlockingQueue和LinkedBlockingQueue分析

        JAVA并发包提供三个常用的并发队列实现,分别是:ConcurrentLinkedQueue.LinkedBlockingQueue和ArrayBlockingQueue. Concurren ...

  7. c#静态成员和静态类

    说起静态类,你可能会联想到实例类.这两者并不难区分,前者(静态类)只在内存中创建一个,而后者(实例类)则是每次实例化后,就会再内存创建一份.今天来简单聊一下静态类的理解. 代码情景: class Pr ...

  8. 【C语言编程练习】5.11 猴子吃桃子问题

    1. 问题描述 有一只猴子,第一天摘下若干个桃子,当即吃掉了一半,又多吃了一个,第二天又将剩下的桃子吃掉了一半,又多吃了一个,按照这样的吃法,每天都吃前一天吃下的桃子的一半又一个.到第十天,就剩下了一 ...

  9. 深入学习css伪类和伪元素及其用法

    前言 CSS的伪类和伪元素在平时的代码中经常会出现,可是一旦别人问你,什么是伪类,什么是伪元素,可能还是不能完整的表述出来,下面我们来一探究竟. 伪类和伪元素定义 伪类用于在页面中的元素处于某个状态时 ...

  10. 你真的了解String的常见API吗?

    面试官Q1:请问String常见的方法有哪些,列举几个? String是我们开发中使用频率最高的类,它有哪些方法,大家一定不会陌生,例如: length();//计算字符串的长度 charAt();/ ...