Spring IOC - FactoryBean源码解析

news2025/4/17 4:31:49

​​​​​1. 介绍

        FactoryBean是Spring框架中的一个接口,它允许我们自定义一个工厂类,用于创建和管理Spring容器中的Bean实例。FactoryBean接口定义了两个方法:

  • getObject():用于返回一个Bean实例,这个方法可以自定义创建Bean实例的逻辑。
  • getObjectType():用于返回创建的Bean实例的类型。

        当我们在Spring中自定义一个FactoryBean时,Spring容器会先创建这个FactoryBean实例,然后调用它的getObject()方法来获取实际的Bean实例。FactoryBean的使用场景比较广泛,例如:

  • 当我们需要创建一个Bean实例时,但是这个Bean实例的创建过程比较复杂,需要进行一些额外的逻辑处理,这时我们可以使用FactoryBean来自定义创建过程。
  • 当我们需要创建多个实例时,但是这些实例的创建逻辑比较相似,只有一些参数不同,这时我们可以使用FactoryBean来封装这些相似的逻辑,避免重复代码。

2. Demo

@Component
public class MyFactoryBean implements FactoryBean {

    @Override
    public Object getObject() throws Exception {
        return new User();
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }
}

public class FactoryBeanTest {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(FactoryBeanTest.class.getPackage().getName());
        System.out.println(annotationConfigApplicationContext.getBean("&myFactoryBean"));
        System.out.println(annotationConfigApplicationContext.getBean("myFactoryBean"));
    }
}

        返回结果如下: 

 3. 现象

  • FactoryBean会创建两个对象
  • beanName为myFactoryBean,实例为自己本身,存储在单例池singletonObjects(一级缓存)中

  • 通过getObject创建的对象,beanName为myFactory,缓存在factoryBeanObjectCache中,它是专门用来缓存通过FactoryBean创建的单例对象的。

  • 上述两个单例池均为ConcurrentHashMap类型,属于Spring容器的一部分。

  • 单例池和factoryBeanObjectCache中的beanName都为myFactory,没有&前缀,该前缀只是决定了从哪个缓冲池取对象

4. 源码解析

注:源码较长,只关注主干逻辑,省略了非主要代码

执行路径:main#getBean -》 AbstractApplicationContext#getBean -》 AbstractBeanFactory#doGetBean -》 AbstracBeanFactory#getObjectForBeanInstance -》 FactoryBeanRegistrySupport#getObjectFromFactoryBean -》FactoryBeanRegistrySupport#doGetObjectFromFactoryBean -》 FactoryBean#getObject

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
   // 如果name以一个或多个&符合开头,则去掉该一个或多个&符号,否则直接返回
   // 如果是别名,则转换成规范名称
   final String beanName = transformedBeanName(name);
   Object bean;

   // Eagerly check singleton cache for manually registered singletons.
   // 从单例池中获取实例,在AnnotationConfigApplicationContext实例化后,此时
   // 单例池中已有键值对:myFactoryBean:MyFactoryBean实例,所以能够获取到
   Object sharedInstance = getSingleton(beanName);
   // 接下来代码基本就是一个if-else分支
   if (sharedInstance != null && args == null) {
      if (logger.isTraceEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
                  "' that is not fully initialized yet - a consequence of a circular reference");
         }
         else {
            logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      // 获取给定bean实例的对象,可以是bean实例本身,也可以是FactoryBean创建的对象
      // 下面会解析该方法
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   } else {
       // else分支在这里可以不用关注
   }
   return (T) bean;
}
protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

   // 如果bean实例是FactoryBean类型或者以&字符开头直接返回
   if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
      return beanInstance;
   }

   Object object = null;
   if (mbd == null) {
      // 从factoryBeanObjectCache缓冲池中获取对象
      object = getCachedObjectForFactoryBean(beanName);
   }
   if (object == null) {
      // Return bean instance from factory.
      FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
      // Caches object obtained from FactoryBean if it is a singleton.
      if (mbd == null && containsBeanDefinition(beanName)) {
         mbd = getMergedLocalBeanDefinition(beanName);
      }
      // 非主逻辑代码,先忽略
      boolean synthetic = (mbd != null && mbd.isSynthetic());
      // 通过回调FactoryBean#getObject获取对象,该方法继续调用doGetObjectFromFactoryBean
      object = getObjectFromFactoryBean(factory, beanName, !synthetic);
   }
   return object;
}
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
      throws BeanCreationException {

   Object object;
   try {
         // 回到factoryBean的getObject方法,返回的对象会缓存到factoryBeanObjectCache池子中
         object = factory.getObject();
   }
   catch (FactoryBeanNotInitializedException ex) {
      throw new BeanCurrentlyInCreationException(beanName, ex.toString());
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
   }

   // Do not accept a null value for a FactoryBean that's not fully
   // initialized yet: Many FactoryBeans just return null then.
   if (object == null) {
      if (isSingletonCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(
               beanName, "FactoryBean which is currently in creation returned null from getObject");
      }
      object = new NullBean();
   }
   return object;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/602772.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

创建型设计模式04-建造者模式

✨作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 建造者模式 1、建造者模式介绍 建造者模式是一种创建型设计模式&#xff0c;用于将一个复杂对象的构造过程与其表示分离开来&…

七面蚂蚁金服,超硬核面经,已拿Offer!!

刚面试了蚂蚁金服&#xff0c;给大家分享下这些硬核的面试经验 一面&#xff1a;50分钟 1.自我介绍 2.之前的实习经历提问 3.实习做过什么项目&#xff0c;负责什么 4.进程线程 5.java的垃圾回收 6.数据库简单sql语句 7.事务隔离 8.测试微信点赞 9.对测试的了解 10.编程&…

【LeetCode每日一题】——2269.找到一个数字的 K 美丽值

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 滑动窗口 二【题目难度】 简单 三【题目编号】 2269.找到一个数字的 K 美丽值 四【题目描述…

第二十二篇、基于Arduino uno,控制五线四相步进电机实现正转和反转——结果导向

0、结果 说明&#xff1a;步进电机可以旋转指定角度&#xff0c;例如转了九十度就停止&#xff0c;如果想一直转也是可以的&#xff0c;程序里面已写&#xff0c;而且也有正反转。如果是你想要的&#xff0c;可以接着往下看。 1、外观 说明&#xff1a;五线四相步进电机如下…

自然语言处理从入门到应用——文本的表示方法

分类目录&#xff1a;《自然语言处理从入门到应用》总目录 若要利用计算机对自然语言进行处理&#xff0c;首先需要解决语言在计算机内部的存储和计算问题。字符串&#xff08;String&#xff09;是文本最自然&#xff0c;也是最常用的机内存储形式。所谓字符串&#xff0c;即字…

rtty移植到MTK OpenWRT平台

准备工具链 使用MTK Openwrt 相应平台工具链&#xff0c;不能使用buildroot的工具链&#xff0c;否则程序无法运行&#xff0c;本文MTK7621为例工具链官网下载地址 构建libev rtty依赖libev库&#xff0c;因此需要先构建libev git clone https://github.com/enki/libev.git …

C语言-外部关键字extern

extern 关键字 extern 用在全局变量或函数的声明前&#xff0c;用来说明“此变量/函数是在别处定义的&#xff0c;要在此处引用。 什么是定义&#xff0c;什么是声明? 什么是定义:所谓的定义就是为这个变量分配一块内存并给它取上一个名字&#xff0c;这个名字就是我们经…

《多传感器融合感知》一、构建自动驾驶多传感器系统

来自 &#xff1a; https://www.bilibili.com/video/BV1JY4y1R7Be/ 多传感器融合的需求多传感器硬件系统构建多传感器硬件系统的时序闭环 1. 多传感器融合的需求 自动驾驶用来解决的问题&#xff1a; 建图定位、感知问题&#xff08;路上有什么、堵不堵、好不好走…&#xff…

数据可视化系列指南之分布类图表大全

导语 随着数据在各行业中的应用越来越广泛&#xff0c;大家也逐渐认识到数据可视化在企业生产经营中的重要作用&#xff0c;在数据可视化过程中&#xff0c;图表是处理数据的重要组成部分&#xff0c;因为它们是一种将大量数据压缩为易于理解的格式的方法。数据可视化可以让受…

生态系统服务评估方法 | 人类活动影响、重大工程实施的生态成效评估(InVEST模型)

以InVEST模型结合实际项目进行由浅入深的实战技术&#xff0c;针对特点及需求进行分析&#xff0c;融合课程体系&#xff0c;对接工作实际项目及论文写作&#xff0c;解决参会者关注的重点及实际项目过程问题&#xff0c;采取逐步延伸的逻辑&#xff0c;不论您是小白亦或是已经…

数据库磁盘文件格式的设计原理内幕

引言 访问磁盘需通过系统调用来实现&#xff0c;因此通常我们需要指定目标文件的偏移量&#xff0c;然后把数据从磁盘上的形式解析成合适主存的形式。这意味着要想设计一个高效的磁盘数据结构&#xff0c;必须构造一种易于修改和解析的文件格式。在本文中&#xff0c;我们将讨论…

unicms 使用thinkphp8 重构版

unicms 有你存在 一切安好 2023年6月使用thinkphp8&#xff0c;重构了cms。php最低使用php8版。参考了多个cms结合10年的开发经验&#xff0c;打造了开箱机用&#xff0c;简单的cms系统。当然由于你能力和开发时间限制&#xff0c;肯定有bug。欢迎大家指正。我也会不定期的修改…

【线性规划模型】

线性规划模型&#xff1a;原理介绍和预测应用 引言 线性规划是运筹学中一种重要的数学优化方法&#xff0c;被广泛应用于各个领域&#xff0c;包括工业、经济、物流等。 线性规划模型的原理 线性规划模型的目标是在一组线性约束条件下&#xff0c;寻找一组变量的最优解&…

Emm_V4.2步进闭环驱动器说明书Rev1.1

一、产品介绍 1.1 产品简介&#xff1a; Emm42_V4.x步进闭环驱动器是张大头智控为满足广大用户需求而自主研发的一款稳定可靠的产品&#xff0c;它是基于上一代Emm42_V3.6版本升级而来&#xff0c;不仅延续了其优秀的FOC矢量闭环控制算法&#xff0c;更在其传统的Dir/Step控制模…

Java003——编写和运行第一个Java程序HelloWorld

一、使用记事本创建Java并运行 1.1、设置文件显示后缀名 目的是为了方便查看文件类型 1.2、创建一个HelloWorld.java文件 java程序文件都是以.java后缀结尾的 1.3、编写Java程序 编写一下程序&#xff0c;并保存 public class HelloWorld {public static void main(Strin…

100天精通Golang(基础入门篇)——第0天: 安装 Go 语言开发环境的基础教程,带你学习Golang之Hello Go !

文章目录 1. 下载 Go 语言的安装包&#xff1a;1. 安装 Go 编译器&#xff1a;- 双击 安装下一步:切换安装路径:下一步:安装等待安装完成安装完成 - 2. 测试安装是否成功&#xff1a;1. 开始学习 Go 语言&#xff1a;1. 创建项目2. 创建 类文件3. 输入类名4. 键入 下方代码块5.…

【C语言】让你不再害怕“指针”【c】【知识点整理】

目录 一.什么是指针&&为什么需要指针&#xff1f; 1.什么是指针&#xff1f; 2.为什么需要指针&#xff1f; 以一个代码为例观察地址&#xff1a;&#xff08;这里我们可以通过调试和打印两种方式观察&#xff09; 1.调试观察&#xff1a; 2.打印观察&#xff08;…

Vue.js 中的 v-if 和 v-show 有什么区别?

Vue.js 中的 v-if 和 v-show 有什么区别&#xff1f; 在 Vue.js 中&#xff0c;v-if 和 v-show 都是用来控制元素的显示和隐藏的指令。但是&#xff0c;它们之间有一些区别。本文将深入探讨 v-if 和 v-show 的区别&#xff0c;并给出一些相关的代码示例。 v-if v-if 是一种条…

python数据分析模块

python数据分析模块 Python与数据分析的关系数据分析常用模块Python数据分析常用类库1&#xff0e;数组计算的数学模块-Numpy2&#xff0e;Pandas——数据分析核心库3&#xff0e;Matplotlib——绘制数据图表的 Python 库4&#xff0e;批量处理Excel文件的模块-xlwings5&#x…

浮点型在内存中的存储,与整型存储方式竟然不同

对于整形的存储方式&#xff0c;大家可以看我的上一篇文章&#xff0c;这篇文章来介绍浮点型在内存中存储方式。 目录 1. 问题 2. 浮点型家族&#xff1a; 3. 存储方式: 4. 读取方式&#xff1a; 5. 解题 1. 问题 我们先来看一段代码&#xff1a; #include <stdio.h&…