Springboot扩展点之FactoryBean
前言
FactoryBean是一个有意思,且非常重要的扩展点,之所以说是有意思,是因为它老是被拿来与另一个名字比较类似的BeanFactory来比较,特别是在面试当中,动不动就问你:你了解Beanfactory和FactoryBean的区别吗?其实两个是完全不同的接口,如果非要说出有什么明显区别,大概就是名字吧。为什么又说非常重要呢?那是因为在创建一些比较复杂的bean的时候,常规的方式不能使用,就可以考虑使用FactoryBean,特别其他框架技术与Spring集成的时候,如mybatis与Spring的集成,大家都知道,mybatis是通过SqlSessionFactory创建出Sqlsession来执行sql的,那么Service层在调用Dao层的接口来执行数据库操作时肯定得持有SqlSessionFactory,那么问题来了:Spring容器怎么才能持有SqlSessionFactory呢?答案就是SqlSessionFactoryBean,它实现了FactoryBean接口。
既然FactoryBean如此神奇,那么就先盘一盘它的主要功能特性,然后再通过一个示例来验证一下,最后再深入盘一盘其工作原理 。
功能特性
1、FactoryBean接口有三个方法:1、getObject(),用于获取bean,主要应用在创建一些复杂的bean的场景;2、getObjectType(),用于获取返回bean的类型;3、isSingleton(),用于判断返回bean是否属于单例,默认trure,通俗说就是工厂bean;
2、BeanFactory是Spring bean容器的根接口,ApplicationContext是Spring bean容器的高级接口,继承于BeanFactory,通俗理解就是生成bean的工厂;
所以FactoryBean与BeanFactory本质上是完全不同的两个接口。既然完全不同,又说什么区别呢?至少我是这么认为的。
实现方式
那么FactoryBean怎么用呢?这里先举一个场景:假如需要要创建一个特别复杂的类Computer
1、定义Computer类;
@Slf4j
public class Computer {private String type;public String getType() {return type;}public void setType(String type) {this.type = type;}public Computer() {log.info("----Computer类无参数构造方法触发执行");}public Computer(String type) {this.type = type;log.info("----Computer类有参数构造方法触发执行");}
}2、定义ComputerFactoryBean,实现FactoryBean接口,实现了getObject(),创建了一个“复杂”的Bean;在getObjectType()返回了Bean的类型;在isSingleton()指定Bean的作用域为单例;
@Component
@Slf4j
public class ComputerFactoryBean implements FactoryBean {private String name = "ComputerFactoryBean本尊";public ComputerFactoryBean() {log.info("----ComputerFactoryBean无参数构造方法触发执行");}@Overridepublic Object getObject() throws Exception {log.info("----com.fanfu.bean.ComputerFactoryBean.getObject()触发执行-start");Computer computer = new Computer("商用笔记本电脑");log.info("----com.fanfu.bean.ComputerFactoryBean.getObject()触发执行-end");return computer;}@Overridepublic Class<?> getObjectType() {return Computer.class;}@Overridepublic boolean isSingleton() {return true;}
}
3、编写单元测试,从Spring容器中取出beanName为"computeFactoryBean"的bean,通常情况下,如果ComputerFactoryBean未实现FactoryBean接口,getBean("computeFactoryBean")的结果是computeFactoryBean对象;而实现了FactoryBean接口,getBean("computeFactoryBean")的结果的是getObject()方法中返回的结果,即computer,这就是FactoryBean如此神奇的地方。
@Test
public void test6() {log.info("----单元测试执行开始");log.info("----Spring容器实例化开始");AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");log.info("----Spring容器实例化完成");ComputerFactoryBean computerFactoryBean = ((ComputerFactoryBean) context.getBean("&computerFactoryBean"));Computer computer = (Computer) context.getBean("computerFactoryBean");Assert.isTrue(computerFactoryBean.getClass().getName().equals("com.fanfu.bean.ComputerFactoryBean"));Assert.isTrue(computer.getClass().getName().equals("com.fanfu.entity.Computer"));log.info("----单元测试执行完毕");
}单元测试结果:

工作原理
从单元测试验证结果来看,ComputerFactoryBean本尊在Spring容器实例化的过程中,就以非懒加载的单例bean实例化完成注册到了Spring容器里。顺着context.getBean("computerFactoryBean")往下跟踪,会发现接着调用了AbstractApplicationContext#getBean(java.lang.String)-->AbstractBeanFactory#getBean(java.lang.String)-->AbstractAutowireCapableBeanFactory#getObjectForBeanInstance()-->AbstractBeanFactory#getObjectForBeanInstance(),到这是一个关键点:
1、AbstractBeanFactory#getObjectForBeanInstance()中判断是否bean是否是一个工厂引用,即beanName是否是“&”开头,如果beanName是以“&”开头,则直接返回本尊;如果不是,则继续向下执行;
2、检查bean是否实现了FactoryBean接口,如果获取的bean没有实现FactoryBean接口,则直接返回;如果获取的bean实现了FactoryBean接口,则继续向下执行;
3、对获取的bean强制转换为FactoryBean,然后去执行FactoryBean接口实现类的getObject();
protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {//检查bean是否是一个工厂引用,即beanName是否是“&”开头if (BeanFactoryUtils.isFactoryDereference(name)) {if (beanInstance instanceof NullBean) {return beanInstance;}if (!(beanInstance instanceof FactoryBean)) {throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());}if (mbd != null) {mbd.isFactoryBean = true;}//如果beanName是以“&”开头,则直接返回本尊return beanInstance;}//如果获取的bean没有实现FactoryBean接口,则直接返回;if (!(beanInstance instanceof FactoryBean)) {return beanInstance;}Object object = null;if (mbd != null) {mbd.isFactoryBean = true;}else {object = getCachedObjectForFactoryBean(beanName);}if (object == null) {// 如果获取的bean实现FactoryBean接口,则对bean进行强制转换FactoryBean<?> factory = (FactoryBean<?>) beanInstance;if (mbd == null && containsBeanDefinition(beanName)) {mbd = getMergedLocalBeanDefinition(beanName);}boolean synthetic = (mbd != null && mbd.isSynthetic());//去执行FactoryBean接口实现类的getObject()object = getObjectFromFactoryBean(factory, beanName, !synthetic);}return object;
}
总结
总体来讲,从FactoryBean的实现方式到工作原理,都很简单,同时也是有大用处的扩展点,特别要必要牢牢掌握。当然,如果有面试官再问你,FactoryBean与BeanFactory有什么区别的时候?不要再犹豫,直接告诉他:”FactoryBean与BeanFactory本身并没有什么区别,但是我可以分别给你介绍介绍这两个接口有什么用处。FactoryBean是一个工厂Bean,在需要创建比较复杂的bean的时候可以用到,BeanFactory是Spring bean容器的根接口,也就是说实现BeanFactory,可以得到一个最基础的Spring容器,Spring中的所有高级容器都继承了这个根接口。“
如果你能这样回答这个问题,相信会给面试官留下一下好印象的。
相关文章:
Springboot扩展点之FactoryBean
前言FactoryBean是一个有意思,且非常重要的扩展点,之所以说是有意思,是因为它老是被拿来与另一个名字比较类似的BeanFactory来比较,特别是在面试当中,动不动就问你:你了解Beanfactory和FactoryBean的区别吗…...
新库上线 | CnOpenDataA股上市公司交易所监管措施数据
A股上市公司交易所监管措施数据 一、数据简介 证券市场监管是指证券管理机关运用法律的、经济的以及必要的行政手段,对证券的募集、发行、交易等行为以及证券投资中介机构的行为进行监督与管理。 我国《证券交易所管理办法》第十二条规定,证券交易所应当…...
同步辐射XAFS表征方法的应用场景分析
X射线吸收精细结构XAFS表征方法是一种用于研究物质结构和化学环境的分析技术。XAFS 使用 X 射线照射到物质表面,并观察由此产生的 X 光吸收谱。 XAFS 技术通常应用于研究高分子物质、生物分子、纳米结构和其他类型的物质。例如,XAFS 可以用来研究高分子…...
06 antdesign react Anchor 不同页面之间实现锚点
react Anchor 不同页面之间实现锚点一、定义二、使用步骤三、开发流程(一)、组件(二)、页面布局(三)、点击事件(四)、总结说明一、react单页面应用,当前页面的锚点二、react单页面应用,不同页面的锚点思路:锚点只能在当前页面使用,…...
mysql调优-内存缓冲池
因本地查询和服务器查询相比服务器慢了很多,同样的数据,同样的sql查询,考虑了是不是链接太多了,自行查询了下,我使用的c3p0的链接池,配置一个小时超时,正常情况下是20多个链接,而mys…...
【LeetCode】每日一题(5)
目录 题目:2341. 数组能形成多少数对 - 力扣(Leetcode) 题目的接口: 解题思路: 代码: 过啦!!! 写在最后: 题目:2341. 数组能形成多少数对 -…...
输入任意多个整数, 把这些数据保存到文件data.txt中.(按ctrl + z)
#pragma once #include <iostream> #include <fstream> using namespace std; /* 输入任意多个整数, 把这些数据保存到文件data.txt中. 如果在输入的过程中, 输入错误, 则提示用户重新输入. 指导用户输入结束(按ctrl z) [每行最多保存10个整数] */ int main() { …...
Mysql数据库的时间(3)一如何用函数插入时间
暂时用下面四个日期函数插入时间 如:insert into Stu(time) values (now()); Mysql的时间函数描述对应的Mysql的时间类型now()/sysdate()NOW()函数以YYYY-MM-DD HH:MM:SS返回当前的日期时间date/time/dateTime/timeStamp/yearcurDate()/current_date()返回当前的日期YYYY-M…...
关于eval函数(将JSON格式的字符串转换成JSON格式对象)
<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>关于eval函数</title> </head> <body> <!--JSON是一种行业内的数据交换格式标准。在JS当中以对象的形式存在…...
2023最强软件测试面试题,精选100 道,内附答案版,冲刺金3银4
精挑细选,整理了100道软件测试面试题,都是非常常见的面试题,篇幅较长,所以只放出了题目,答案在评论区! 测试技术面试题 1、什么是兼容性测试?兼容性测试侧重哪些方面? 2、我现在有…...
一文搞懂Docker容器里进程的 pid 是如何申请出来的?
如果大家有过在容器中执行 ps 命令的经验,都会知道在容器中的进程的 pid 一般是比较小的。例如下面我的这个例子。 # ps -ef PID USER TIME COMMAND1 root 0:00 ./demo-ie13 root 0:00 /bin/bash21 root 0:00 ps -ef 不知道大家是否和我一样…...
若依框架如何新增自定义主题风格
若依框架新增主题风格1.实现结果2.实现步骤2.1Settings目录下2.2 variables.scss2.3 sidebar.scss2.4 Logo.vue2.5 Siderbar目录下的index.vue1.实现结果 2.实现步骤 需要改动的文件目录: 2.1Settings目录下 <div class"setting-drawer-block-checbox-it…...
C语言格式化输入和输出; Format格式化
Format格式化 %1s或者%2s,%3s:取字符串的前1,2或者3位。%*c:屏蔽一个字符。%[A-Z]:取一个A到Z的值。 %[^a-z]:不取a到z的值。 %[^\n]:取非换行之前的值。printf("%5d", a):左边补 格式化:有正则在其中。 int main() {printf("%5d\n&quo…...
Revit教程:怎么关掉工具栏的实时提示?
一、Revit中如何关闭工具栏的实时帮助提示 如图1所示,Revit会对每一个命令有一个简单的图文说明,方便不熟悉软件的用户使用。对于已经熟悉软件的用户,会觉得鼠标在菜单上悬停时弹出的实时帮助页面很干扰使用,而且很占内存资源&…...
javascript 简介
JavaScript 是互联网上最流行的脚本语言,这门语言可用于 HTML 和 web,更可广泛用于服务器、PC、笔记本电脑、平板电脑和智能手机等设备。JavaScript 是脚本语言JavaScript 是一种轻量级的编程语言。JavaScript 是可插入 HTML 页面的编程代码。JavaScript…...
医学图象分割常用损失函数(附Pytorch和Keras代码)
对损失函数没有太大的了解,就是知道它很重要,搜集了一些常用的医学图象分割损失函数,学习一下! 医学图象分割常见损失函数前言1 Dice Loss2 BCE-Dice Loss3 Jaccard/Intersection over Union (IoU) Loss4 Focal Loss5 Tvesky Loss…...
【新2023】华为OD机试 - 病菌感染(Python)
华为 OD 清单查看地址:blog.csdn.net/hihell/category_12199275.html 病菌感染 题目 在一个地图中(地图有N*N个区域组成) 有部分区域被感染病菌 感染区域每天都会把周围上下左右的四个区域感染 请根据给定的地图计算多少天以后全部区域都会被感染 如果初始地图上所有区域都…...
QGIS中进行批量坡向计算
QGIS中进行坡向计算1. 坡向计算中的Z因子(垂直单位与水平单位的比值)2. 坡向计算步骤坡度计算的姊妹篇–坡向计算来了 1. 坡向计算中的Z因子(垂直单位与水平单位的比值) z 因子是一个转换因子,当输入表面的垂直坐标&…...
Redis持久化机制
一、RDB RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。 RDB是Redis默认的持久化机制 - RDB持久化文件,速度比较快,而且存储的是一个二进制的文件,传输起来很方便。 - RD…...
2、VUE面试题
1, 如何让CSS只在当前组件中起作用?在组件中的style前面加上scoped2、<keep-alive></keep-alive>的作用是什么?keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。3, vue组件中如何获取dom元素?使…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...
