obtainFreshBeanFactory()方法概述

定义BeanFactory,并加载以下两种bean的定义,装配到BeanFactory:

1.配置文件中定义的bean

2.通过<context:component-scan base-package="..." />配置的路径下的,且经过相应注解标注的所有类,注解包括:@Controller、@Service、@Component、@Repository

源码解读

主要流程总结:

1.创建BeanFactory:DefaultListableBeanFactory

2.解析web.xml配置,读取spring配置文件,封装为Resource对象

3.把Resource对象封装为Document对象

4.开始层层遍历Document的节点。

以下是细节:

先来看该方法的实现,注:这里会把无关代码删掉,以方便阅读。

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//刷新bean工厂
     this.refreshBeanFactory();
    //创建bean工厂
    return this.getBeanFactory();
 } 

重点看刷新bean工厂部分:

protected final void refreshBeanFactory() throws BeansException {
       //创建bean工厂
DefaultListableBeanFactory beanFactory = createBeanFactory();
       //这里加载beanDefinition,并赋给bean工厂
       loadBeanDefinitions(beanFactory);
}
createBeanFactory()好理解,就是new了个工厂对象。
有了工厂对象后,就需要往里面装载东西,装什么呢?这里是

接下来看loadBeanDefinitions(beanFactory)方法的具体实现:创建xml文件读取器

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 以下这一堆内容就是为了准备一个xml文件读取器,仅作了解
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
     beanDefinitionReader.setEnvironment(this.getEnvironment());
     beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));   initBeanDefinitionReader(beanDefinitionReader);
    //这里才是核心,加载beanDefinition的工作还没开始
loadBeanDefinitions(beanDefinitionReader);
}

继续跟进去,这里依然“没干正事”:加载spring配置文件

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
  //核心代码
    reader.loadBeanDefinitions(configLocations);
}
}

接着看核心代码

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader instanceof ResourcePatternResolver) {
// 通配符模式匹配资源,转换为Resource对象。spring提供了多种ResourceLoader,根据通配符匹配,生成对应类型的Resource
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
          //【继续把加载工作往后放】
          int loadCount = loadBeanDefinitions(resources);return loadCount;
}
}
else {
// 以绝对路径加载单个资源文件,转换为Resource对象
Resource resource = resourceLoader.getResource(location);
       //【继续把加载工作往后放】
int loadCount = loadBeanDefinitions(resource);return loadCount;
}
}

通过上面一步,把配置资源转化为Resource对象,然后作为参数传入loadxxx方法里进行解析。

进入下面的实现发现,依然在做准备工作:将Resource读取为流

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
//
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//终于到do...是不是这里就开始真正的执行加载了?
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}

来看下,删除非核心代码,就做了两件事,先读取资源对象Resource,封装成Document对象;再“注册”beanDefinition。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
    //生成Document对象
Document doc = doLoadDocument(inputSource, resource);
        //注册BeanDefinition
return registerBeanDefinitions(doc, resource); }

中间又经历了n个准备环境,最终进入方法parseBeanDefinitions,拿到了Document对象的根节点,开始调用解析方法解析节点:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

具体的解析逻辑,可以参考以下文章:https://blog.csdn.net/v123411739/article/details/86669952

BeanDefinition包含的主要内容:

@todo

解析完成后,依然是注入到BeanFactory中缓存起来,供后续使用,主要的内容是两部分:

1.beanDefinitionNames

2.beanDefinitionMap

总结:

obtainFreshBeanFactory()方法的主要作用:
1.创建beanFactory
2.根据web.xml中contextConfigLocation配置的路径,读取Spring配置文件,封装为Resource
3.根据Resource加载XML配置文件(bean文件)并解析为Document对象
4.遍历Document,解析为beanDefinition。

【学习底层原理系列】重读spring源码3-加载beanDefinition的方法obtainFreshBeanFactory的更多相关文章

  1. 【学习底层原理系列】重读spring源码1-建立基本的认知模型

    开篇闲扯 在工作中,相信很多人都有这种体会,与其修改别人代码,宁愿自己重写. 为什么? 先说为什么愿意自己写: 从0-1的过程,是建立在自己已有认知基础上,去用自己熟悉的方式构建一件作品.也就是说, ...

  2. 线程池底层原理详解与源码分析(补充部分---ScheduledThreadPoolExecutor类分析)

    [1]前言 本篇幅是对 线程池底层原理详解与源码分析  的补充,默认你已经看完了上一篇对ThreadPoolExecutor类有了足够的了解. [2]ScheduledThreadPoolExecut ...

  3. 深入理解 spring 容器,源码分析加载过程

    Spring框架提供了构建Web应用程序的全功能MVC模块,叫Spring MVC,通过Spring Core+Spring MVC即可搭建一套稳定的Java Web项目.本文通过Spring MVC ...

  4. 读spring源码(二)-XmlBeanDefinitionReader-解析BeanDefinition

    上次说到ApplicationContext加载BeanDefinition时会创建一个XmlBeanDefinitionReader,将XML解析.BeanDefinition加载委托给XmlBea ...

  5. 2.2 spring5源码 -- ioc加载的整体流程

    之前我们知道了spring ioc的加载过程, 具体如下图. 下面我们就来对照下图, 看看ioc加载的源代码. 下面在用装修类比, 看看个个组件都是怎么工作的. 接下来是源码分析的整体结构图. 对照上 ...

  6. 深入Spring之IOC之加载BeanDefinition

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

  7. HashMap底层原理及jdk1.8源码解读

    一.前言 写在前面:小编码字收集资料花了一天的时间整理出来,对你有帮助一键三连走一波哈,谢谢啦!! HashMap在我们日常开发中可谓经常遇到,HashMap 源码和底层原理在现在面试中是必问的.所以 ...

  8. jquery源码 DOM加载

    jQuery版本:2.0.3 DOM加载有关的扩展 isReady:DOM是否加载完(内部使用) readyWait:等待多少文件的计数器(内部使用) holdReady():推迟DOM触发 read ...

  9. spring源码 继承AttributeAccessor的BeanDefinition接口

    /** * A BeanDefinition describes a bean instance, which has property values, * constructor argument ...

随机推荐

  1. Vue学习笔记-API调试工具--->国产apipost按装(比postman好按装好用)

    一  使用环境: windows 7 64位操作系统 二   Vue学习笔记-API调试工具--->apipost按装 1.下载: https://www.apipost.cn/ (比postm ...

  2. Java基础语法:运算符

    Java 运算符(operator)根据功能分类: 算术运算符:+,-,*,/,%,++,-- 赋值运算符:= 关系运算符:>,<,>=,<=,==,!=,instanceof ...

  3. 看完我的笔记不懂也会懂----git

    Git学习笔记 - 什么是Git - 首次使用Git - DOS常用命令 - Git常用命令 - 关于HEAD - 版本回退 - 工作区.暂存区与版本库 - git追踪的是修改而非文件本身 - 撤销修 ...

  4. PAT-1152(Google Recruitment)字符串+素数

    Google Recruitment PAT-1152 本题最需要注意的是最后输出要以字符串形式输出,否则可能会出现前导0的情况. /** * @Author WaleGarrett * @Date ...

  5. HDOJ-4725(Dijikstra算法+拆点求最短路)

    The Shortest Path in Nya Graph HDOJ-4725 这题是关于最短路的问题,但是和常规的最短路有点不同的就是这里多了层次这一结构. 为了解决这一问题可以把每一层抽象或者划 ...

  6. 在Asp.Net Core 5 中使用EF Core连接MariaDB

    升级到Asp.Net Core 5,使用EF Core连接MariaDB,使用的Nuget包Pomelo.EntityFrameworkCore.MySql也升级到了5.0.0-alpha.2,然后发 ...

  7. 基于CefSharp开发浏览器(九)浏览器历史记录弹窗面板

    一.前言 前两篇文章写的是关于浏览器收藏夹的内容,因为收藏夹的内容不会太多,故采用json格式的文本文件作为收藏夹的存储方式. 关于浏览器历史记录,我个人每天大概会打开百来次网页甚至更多,时间越长历史 ...

  8. 001-HashMap源码分析

    HashMap源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如 memcached)的核心其实就是在内存中维护一张大的哈希表. 一.什 ...

  9. RSA典型非对称加密算法

    私钥加密-->公钥解密,反之亦然,但不安全.也可以当做数字签名. public class RSACoder {         //非对称加密算法         public static  ...

  10. Bug调试专项练习三笔记

    前言:大家需要将文件夹中"有问题的代码" 导入到自己的工作空间中一. 训练一: 正确效果:首先要求大家导入给大家的项目, 给项目的"虚拟路径" 设定为" ...