Spring 源码解析 - FactoryBean 获取 Bean 过程

news2024/11/6 17:35:41

一、FactoryBean

FactoryBeanSpring框架提供的一个核心接口之一,用来创建复杂或无法通过默认构造函数创建的对象。这种情况下通过实现FactoryBean接口,可以自定义实例化Bean的过程,包括Bean的对象类型、初始化、销毁等。

在应用场景中,可以使用 FactoryBean 集成第三方框架、开源库或是处理一些特殊的业务需求,将Bean的控制权交由我们灵活控制。

比如:通过FactoryBean 将创建对象的逻辑封装起来,而对于使用者则无感知,正常向 IOC 容器获取 Bean 即可。

如果想要获取 FactoryBean 自身的话,则可以在 beanName 前面加个 & 符号,这点我们会在后面的源码分析中体验出来。

下面是 FactoryBean 的组成:

public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
	
    @Nullable
    T getObject() throws Exception;

    @Nullable
    Class<?> getObjectType();

    default boolean isSingleton() {
        return true;
    }
}

对应三个核心方法的说明:

  • isSingleton(): 获取由FactoryBean创建的Bean实例是单例还是原型,默认为单例。
  • T getObject(): 获取由FactoryBean创建的Bean实例,如果isSingleton的作用域为单例,会将该实例进行缓存。
  • Class<?> getObjectType(): 获取由FactoryBean要创建Bean的类型。

下面简单体验下 FactoryBean 的使用:

@Component
public class FactoryBeanTest implements FactoryBean {

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

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

    @Override
    public boolean isSingleton() {
        return true;
    }

    /**
     * 实际Bean
     */
    public static class TestABC{
    
    }
}

测试:

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("com.example.demo.bean");
        System.out.println(context.getBean("factoryBeanTest"));
        System.out.println(context.getBean("&factoryBeanTest"));
    }
}

在这里插入图片描述

可以看出如果以 & 符号开头则获取的是 FactoryBean 自身。

下面从源码的角度分析下,context.getBean 获取 Bean 的过程中是如何处理 FactoryBean 的。

二、源码解读

在本专栏的前面文章中有针对 Bean 创建过程进行源码分析,如果了解的应该知道实际 Bean 创建的逻辑在 AbstractBeanFactorydoGetBean 方法中,这里也是从 doGetBean 方法入手,如果对 Bean 创建过程不了解也可以参考下面这篇文章:

Spring 源码解析 - Bean创建过程 以及 解决循环依赖

doGetBean 方法中,首先会去单例池中查找是否存在,如果存在这里会使用 getObjectForBeanInstance 获取到最终的 Bean 实例:

在这里插入图片描述

如果此时单例池中没有的话,进行下面 Bean 实例初始化的过程,当实例化完成后同样也使用 getObjectForBeanInstance 获取到最终的 Bean 实例

在这里插入图片描述

这个方法看似不起眼,其实对于FactoryBean 起着关键的作用,下面就主要看下 getObjectForBeanInstance 方法:

protected Object getObjectForBeanInstance(
		Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
	// Don't let calling code try to dereference the factory if the bean isn't a factory.
	// 当前bean的名称是否是工厂模式, 主要判断名称是否以 & 开头,如果是以 & 开头,说明当前获取的是 FactoryBean 本身
	if (BeanFactoryUtils.isFactoryDereference(name)) {
		// 如果是 NullBean 直接返回
		if (beanInstance instanceof NullBean) {
			return beanInstance;
		}
		// 如果不是 FactoryBean 的话则抛出异常
		if (!(beanInstance instanceof FactoryBean)) {
			throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
		}
		if (mbd != null) {
			mbd.isFactoryBean = true;
		}
		// 返回 FactoryBean 自身
		return beanInstance;
	}

	// Now we have the bean instance, which may be a normal bean or a FactoryBean.
	// If it's a FactoryBean, we use it to create a bean instance, unless the
	// caller actually wants a reference to the factory.
	// 当前的 bean实例不是 FactoryBean,则直接返回
	if (!(beanInstance instanceof FactoryBean)) {
		return beanInstance;
	}

	// 是 FactoryBean 的情况下
	Object object = null;
	if (mbd != null) {
		mbd.isFactoryBean = true;
	}
	else {
		// 从缓存中获取
		object = getCachedObjectForFactoryBean(beanName);
	}
	if (object == null) {
		// 转为 FactoryBean 类型
		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 实际的 Bean
		object = getObjectFromFactoryBean(factory, beanName, !synthetic);
	}
	return object;
}

在该方法中,首先第一步对 name 进行判断,如果是以 & 开头,说明当前获取的是 FactoryBean 本身,如果此时 Bean 不是 NullBean 也不是 FactoryBean 则只有报错了。

下面如果不是 FactoryBean 类型,直接返回当前实例即可。

再下面逻辑肯定是 FactoryBean 类型了,如果缓存中不存在话则最终调用了 getObjectFromFactoryBean 方法来获取 Bean ,下面看到该方法中:

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // 如果 FactoryBean 里的 Bean 是单例模型, 并且单例池中存在
    if (factory.isSingleton() && containsSingleton(beanName)) {
        // 对单例池加锁
        synchronized (getSingletonMutex()) {
            //尝试从缓冲中获取Bean
            Object object = this.factoryBeanObjectCache.get(beanName);
            // 如果缓存中不存在
            if (object == null) {
                // 从 FactoryBean 中的 getObject() 获取 Bean
                object = doGetObjectFromFactoryBean(factory, beanName);
                // Only post-process and store if not put there already during getObject() call above
                // (e.g. because of circular reference processing triggered by custom getBean calls)
                // 再次尝试获取缓存
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    // 如果缓存存在使用缓存中的Bean
                    object = alreadyThere;
                } else {
                    // 缓存中依然不存在,是否需要触发后通知
                    if (shouldPostProcess) {
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            // Temporarily return non-post-processed object, not storing it yet..
                            return object;
                        }
                        beforeSingletonCreation(beanName);
                        try {
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        } catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                    "Post-processing of FactoryBean's singleton object failed", ex);
                        } finally {
                            afterSingletonCreation(beanName);
                        }
                    }
                    // 加入缓存
                    if (containsSingleton(beanName)) {
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            // 返回实际的 Bean
            return object;
        }
    } else {
        // 如果不是单例模式 或 单例池中不存在
        // 从 FactoryBean 中获取 Bean
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        // 是否需要触发后通知
        if (shouldPostProcess) {
            try {
                object = postProcessObjectFromFactoryBean(object, beanName);
            } catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        // 返回实际的 Bean
        return object;
    }
}

这里有两个分支,FactoryBean 中的 Bean 是单例还是不是单例,也就是 isSingleton 方法是 true 还是 false ,如果是单例的话,则尝试通过 factoryBeanObjectCache 获取缓存对象,从这里就可以看出 FactoryBean 中的 Bean 的单例缓存是存在了 factoryBeanObjectCache 中,而不是 Spring 的单例池。

在该方法中实际获取 Bean 是通过 doGetObjectFromFactoryBean 方法,如果单例模式则将 Bean 放入 factoryBeanObjectCache 中缓存,如果不是单例,则直接返回获取到的 Bean ,下面看到 doGetObjectFromFactoryBean 方法中:

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
    Object object;
    try {
        // 是否有安全管理
        if (System.getSecurityManager() != null) {
            AccessControlContext acc = getAccessControlContext();
            try {
                object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
            } catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        } else {
            // 调用 FactoryBean 中的 getObject 获取实际 Bean。
            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;
}

这里就比较直观了,通过调用 FactoryBeangetObject 方法获取到最终的 Bean 实例。

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

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

相关文章

RK3588平台开发系列讲解(进程篇)程序的二进制格式 ELF

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、ELF 文件二、二进制文件组成三、运行程序为进程沉淀、分享、成长,让自己和他人都能有所收获!😄 📢CPU 是不能执行文本文件里面的指令的,这些指令只有人能看懂,CPU 能够执行的命令是二进制的,比如“0101”…

Selenium还能这么玩:自动管理浏览器

这是个系列文章&#xff0c;主要讲selenium一些实战操作&#xff0c;使用 Python 编写代码。可以把他们应用到自动化测试&#xff0c;也可以应用到网络爬虫中。 这篇文章介绍一个操作&#xff0c;可以让selenium 控制浏览器共用同一个 session。他的应用场景是&#xff1a;共用…

pwn3-绕过防御-ROP(1)

**ROP&#xff1a;**全程Return Oriented Programming(面向返回的编程)&#xff0c;在栈溢出基础上&#xff0c;利用程序中已有的小片段(gadgets)&#xff0c;改变寄存器或变量的值&#xff0c;从而控制程序执行流程&#xff0c;从而绕过NX防御&#xff0c;常见有ret2text,ret2…

Linux:samba服务 (smbd)

smb服务器为centos7 使用的yum安装&#xff08;如果不会搭建本地yum仓库可以查看&#xff09; Linux&#xff1a;rpm查询安装 && yum安装_鲍海超-GNUBHCkalitarro的博客-CSDN博客 samba 简介 samba 使用了 1.SMB 协议 Server Message Block&#xff0c;服务消息块 2…

【LeetCode】769. 最多能完成排序的块

769. 最多能完成排序的块&#xff08;中等&#xff09; 方法一&#xff1a;贪心 思路 由于arr是[0,..., n-1] 的一个排列&#xff0c;若已遍历过的数中的最大值 max 与当前遍历到的下标相等&#xff0c;说明可以进行一次分割&#xff0c;累加答案。 代码 class Solution { …

javaScript蓝桥杯---用什么来做计算

目录 一、介绍二、准备三、目标四、代码五、完成 一、介绍 古以算盘作为计算工具。算盘常为木制矩框&#xff0c;内嵌珠子数串&#xff0c;定位拨珠&#xff0c;可做加减乘除等运算。站在前人的肩膀上&#xff0c;后人研究出计算器&#xff0c;便利了大家的生活&#xff0c;我…

从贝叶斯派的角度去看L1和L2

前沿 推导的两个角度 带约束条件的优化求解&#xff08;拉格朗日乘子法&#xff09;贝叶斯学派的&#xff1a;最大后验概率 理解的两个角度 贝叶斯学派的角度&#xff0c;L2参数符合高斯先验&#xff0c;L1参数符合laplace先验。从有约束问题角度&#xff0c;用拉格朗日转换…

一“幕”了然 ,平行云助力中车打造“掌上工厂”(文末活动报名)

“全球工业生产效率提高1%&#xff0c;成本降低300亿。” 所以工业界有这样一种说法&#xff0c;叫做“工业领域的1%革命”。 Gartner曾预言&#xff1a;到2021年&#xff0c;将有一半的大型工业公司使用数字孪生技术&#xff0c;而这可以使这些公司的生产有效性提高10%。如今…

rust学习 - 构建mini 命令行工具

rust 的运行速度、安全性、单二进制文件输出和跨平台支持使其成为构建命令行程序的最佳选择。 实现一个命令行搜索工具grep,可以在指定文件中搜索指定的字符串。想实现这个功能呢&#xff0c;可以按照以下逻辑流程处理&#xff1a; 获取输入文件路径、需要搜索的字符串读取文…

基于Alexnet网络实现猫狗数据集分类(Keras框架)

目录 1、作者介绍2、Alexnet网络2.1 网络介绍2.2 AlexNet网络的主要特点 3、基于Alexnet网络实现猫狗数据集分类3.1 猫狗大战数据集3.2 数据集处理3.3 准备工作3.4 训练过程3.4 对比实验3.4.1 HALCON平台下的Alexnet实验3.4.2 HALCON平台下的Resnet-50对比实验3.4.3 HALCON平台…

进程的通信——管道和共享内存

进程间的通信有很多种 管道 匿名管道pipe 命名管道 System V IPC System V 消息队列 System V 共享内存 System V 信号量 POSIX IPC 消息队列 共享内存 信号量 互斥量 条件变量 读写锁 这篇文章主要介绍管道和共享内存 管道 管道内核数据结构&#xff1a;在Linux2.6中 struct …

搭建自动化测试环境

目录 1、安装Python并配置环境变量。2、安装Pycharm开发工具。3、安装Selenium4、安装浏览器&#xff1a;Chrome和Firefox的其中之一。5、浏览器驱动&#xff1a;下载Chrome浏览器驱动或者是Firefox浏览器驱动。6、配置webdriver公众号粉丝福利 自动化测试环境&#xff1a; Pyt…

接口反应慢优化

遇到某个功能&#xff0c;页面转圈好久&#xff0c;需要优化 1.F12 查看接口时间 2.看参数 总共耗时9.6s Waiting for sercer response 时间是2秒 Content Download 7秒 慢在Content Download F12查看接口响应 显示Failed to load response data:Request content was e…

这些10款优秀的交互设计软件,你知道吗?

交互软件可以帮助设计师从“可用性”和“用户体验”的角度优化他们的作品。如果设计师想创建一个令人满意的交互设计作品&#xff0c;一个方便的交互设计软件是必不可少的。 根据设计师的个人喜好和方便&#xff0c;选择易于使用的交互设计软件来完成创建。本文盘点十款易于使…

【P55】JMeter 图形结果(Graph Results)

文章目录 一、图形结果&#xff08;Graph Results&#xff09;参数说明二、准备工作三、测试计划设计 一、图形结果&#xff08;Graph Results&#xff09;参数说明 可以以图形的方式查看和分析相关指标 使用场景&#xff1a;一般在调试测试计划期间用来查看相关指标&#xf…

微信小程序基础使用-请求数据并渲染

小程序基本使用-请求数据并渲染 小程序模板语法-数据绑定 在js中定义数据 Page({data: {isOpen: true,message: hello world!} })小程序的data是一个对象&#xff0c;不同于vue的data是一个函数 在模块中获取使用数据 小程序中使用 {{}} 实现数据与模板的绑定 内容绑定&a…

Qt Quick-QML地图引擎之v4版本(新增3D模型/抗锯齿任意多边形下载)

在上个版本Qt quick-QML地图引擎之v4版本(新增3D模型/高德/谷歌在线/离线预览/多线程离线裁剪下载/区域查询/位置搜索/路径规划)_qt 高德地图离线_诺谦的博客-CSDN博客更新了很多小功能。经过朋友们一致需求建议&#xff0c;所以V4继续优化。 B站视频&#xff1a; Qt Quick-QML…

【办公效率提升】Window10与ubuntu递归列出当前目录及其所有子目录中的文件和文件夹

在Windows操作系统中&#xff0c;没有内置的类似于Ubuntu的"tree"命令的功能。但是&#xff0c;你可以使用以下两种方法来实现相似的功能&#xff1a; 方法一&#xff1a;使用dir命令和递归 你可以使用Windows的内置命令"dir"以及递归参数"/s"来…

前后端交互二、form表单与模板引擎

零、文章目录 前后端交互二、form表单与模板引擎 1、form表单的基本使用 HTML相关知识请参考HTML入门 &#xff08;1&#xff09;表单是什么 表单在网页中主要负责数据采集功能。HTML中的<form>标签&#xff0c;就是用于采集用户输入的信息的&#xff0c;并通过<…

linux PerfCollect收集日志及perfview分析

Perfview&#xff1a;https://github.com/Microsoft/perfview/releases PerfCollect&#xff1a;https://github.com/dotnet/coreclr/blob/master/Documentation/project-docs/linux-performance-tracing.md Linux 环境中运行的 ASP.NET Core应用中收集跟踪 PerfCollect&#…