找入口

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篇 - BeanDefinition 的加载阶段(XML 文件)

    该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...

随机推荐

  1. Jmeter学习—004—使用代理录制脚本—HTTP代理服务器(APP、web皆可)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/mmmmmmm_2niu/article/details/78136253记得我最开始使用jmeter ...

  2. Hadoop YARN上运行MapReduce程序

    (1)配置集群 (a)配置hadoop-2.7.2/etc/hadoop/yarn-env.sh 配置一下JAVA_HOME export JAVA_HOME=/home/hadoop/bigdata ...

  3. 存储过程DT参数

    public static void TableValuedToDB(DataTable dt, string storedProcName, string TypeName) { using (Sq ...

  4. 关于AXI_Quad_SPI的寄存器配置

    关于AXI_Quad_SPI的寄存器配置 1.核初始化配置 首先是: 40:0000_000A 1C:8000_0000 28:0000_0004 2.命令与dummy_data 60:000001E ...

  5. CentOS 服务器安全设置 --摘抄自https://www.kafan.cn/edu/8169544.html

    一.系统安全记录文件 操作系统内部的记录文件是检测是否有网络入侵的重要线索.如果您的系统是直接连到Internet,您发现有很多人对您的系统做Telnet/FTP登录尝试,可以运行”#more /va ...

  6. Console下Pause效果的函数

    function PauseConsole(Prompt: PAnsiChar): boolean;var  hStdIn, hStdOut: THandle;  dwRd, dwWr, i: Car ...

  7. Random 中的种子怎么理解

    种子就是生成随机数的根,就是产生随机数的基础.计算机的随机数都是伪随机数,以一个真随机数(种子)作为初始条件,然后用一定的算法不停迭代产生随机数.Java项目中通常是通过Math.random方法和R ...

  8. Importing multi-valued field into Solr from mySQL using Solr Data Import Handler

    http://stackoverflow.com/questions/20233837/importing-multi-valued-field-into-solr-from-mysql-using- ...

  9. python 判断返回结果 in用法

    AA=data[0]["content"] if U"已签收" in AA:(判断 AA里面有没有包含 "已签收的字样") print &q ...

  10. Notepad++ 列操作

    在网上找到一篇关于socket编程的文章,想把其中的代码直接拷贝下来运行测试,但是人家网站做的不够人性化,每行的开头都有行号,直接拷贝就要一行行的删除,甚是麻烦,想到linux下的vi编辑器可以完成列 ...