FactoryBean 原理简介
FactoryBean 首先是一个工厂类,它可以生产指定的Bean,特殊之处在于它可以向Spring容器中注册两个Bean,一个是它本身,一个是FactoryBean.getObject()方法返回值所代表的Bean。通过实现 FactoryBean 接口,你可以控制某个 Bean 的实例化过程,提供比默认机制更复杂的创建逻辑。
FactoryBean接口
public interface FactoryBean<T> {@NullableT getObject() throws Exception;@NullableClass<?> getObjectType();default boolean isSingleton() {return true;}
}
FactoryBean 接口包含三个核心方法:
- Object getObject() throws Exception:
返回由 FactoryBean 创建的 bean 实例。这是 FactoryBean 最重要的方法,通过这个方法,你可以自定义 bean 的创建逻辑。 - Class<?> getObjectType():
返回由 FactoryBean 创建的 bean 实例的类型。这个方法用于告诉 Spring 容器这个工厂 bean 创建的对象的类型,以便在需要时进行类型转换和检查。 - boolean isSingleton():
返回由 FactoryBean 创建的 bean 实例是否是单例的。如果返回 true,那么 Spring 容器会将创建的对象作为单例进行管理;如果返回 false,每次请求都会创建一个新的实例。
重要事项
- FactoryBean表现的是一个工厂的职责。 即一个Bean A如果实现了FactoryBean接口,那么A就变成了一个工厂,根据A的名称获取到的实际上是工厂调用getObject()返回的对象,而不是A本身,如果要获取工厂A自身的实例,那么需要在名称前面加上’&'符号。详情请看实例演示。
实例演示
定义学生类
注意这个类并没有被@Component等注解标注,即没有被Spring容器管理。
public class Student {private Logger logger = LoggerFactory.getLogger(Student.class);public Student(){logger.info("Hi, I am a good student!");}
}
定义StudentFactoryBean
package com.example.jaytecharchite.factorybeandemo;
@Component
public class StudentFactoryBean implements FactoryBean {@Overridepublic Object getObject() throws Exception {return new Student();}@Overridepublic Class<?> getObjectType() {return Student.class;}@Overridepublic boolean isSingleton() {// return false; 非单例return FactoryBean.super.isSingleton(); //默认true,单例工厂}
}
很简单,我们让这个工厂类生成学生类,注意看getObjectType()返回的Class类型和getObject()返回的实例一致,其实也可以不一致,当我们问Spring容器通过Student student = applicationContext.getBean(Student.class);这一句代码向Spring容器获取Bean时,其实它是通过getObjectType()来找到生产这个类型的工厂,从而调用getObject()获取到实例。
配置类
需要配置类将StudentFactoryBean注册到Spring容器中。
@Configuration
@ComponentScan("com.example.jaytecharchite")
public class AppConfig {
}
测试
@SpringBootTest
public class MainTest {private Logger logger = LoggerFactory.getLogger(CallBackTest.class);@Testpublic void test(){AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);logger.info("容器启动完成!");Student student = applicationContext.getBean(Student.class);System.out.println(student);Student student2 = applicationContext.getBean(Student.class);System.out.println(student2);Object student3 = applicationContext.getBean("studentFactoryBean");System.out.println(student3);Object customerFactoryBean2 = applicationContext.getBean("&studentFactoryBean");System.out.println(customerFactoryBean2);}
}
输出
如果isSingleton()返回的是true,则输出结果如下:
2024-07-03 22:37:38.961 INFO 21112 — [ main] c.e.j.xxxaware.CallBackTest : 容器启动完成!
2024-07-03 22:37:38.962 INFO 21112 — [ main] c.e.j.factorybeandemo.Student : Hi, I am a good student!
com.example.jaytecharchite.factorybeandemo.Student@5c887052
com.example.jaytecharchite.factorybeandemo.Student@5c887052
com.example.jaytecharchite.factorybeandemo.Student@5c887052
com.example.jaytecharchite.factorybeandemo.StudentFactoryBean@55fdf7f9
可以看见前三个获取到的Bean都是getObejct()方法的Bean,并且是同一个Bean,因为是设置了单例模式。而最后一个使用了&符号获取到的才是 StudentFactoryBean本身的实例。
如果isSingleton()返回的是false,则输出结果如下:
2024-07-03 22:40:58.104 INFO 4416 — [ main] c.e.j.xxxaware.CallBackTest : 容器启动完成!
2024-07-03 22:40:58.105 INFO 4416 — [ main] c.e.j.factorybeandemo.Student : Hi, I am a good student!
com.example.jaytecharchite.factorybeandemo.Student@72b6832e
2024-07-03 22:40:58.105 INFO 4416 — [ main] c.e.j.factorybeandemo.Student : Hi, I am a good student!
com.example.jaytecharchite.factorybeandemo.Student@3850e90c
2024-07-03 22:40:58.105 INFO 4416 — [ main] c.e.j.factorybeandemo.Student : Hi, I am a good student!
com.example.jaytecharchite.factorybeandemo.Student@3d9f5016
com.example.jaytecharchite.factorybeandemo.StudentFactoryBean@7e91ed74
可以看见前三个Bean都是不同的对象实例,并且每个实例都会执行一次构造方法。我们自定义的StudentFactoryBean实现了FactoryBean接口,所以当StudentFactoryBean被扫描进Spring容器时,实际上它向容器中注册了两个bean,一个是StudentFactoryBean类的单例对象;另外一个就是getObject()方法返回的对象,在demo中,我们重写的getObject()方法中,我们通过new Student()返回了一个Student的实例对象,所以我们从容器中能获取到Student的实例对象。如果我们想通过beanName去获取StudentFactoryBean的单例对象,需要在beanName前面添加一个&符号,这样就能根据beanName获取到原生对象了。否则获取到的还是getObject()提供的对象。
FactoryBean 的使用场景
FactoryBean 的使用场景包括但不限于:
- 创建复杂对象:
当对象的创建过程非常复杂,无法通过简单的构造函数或静态工厂方法实现时,可以使用 FactoryBean。例如,创建带有复杂初始化逻辑的数据库连接对象、远程服务代理等。 - 动态代理:
使用 FactoryBean 可以方便地创建动态代理对象,特别是在 AOP(面向切面编程)和远程调用场景中。 - 单例和多例模式:
通过 isSingleton 方法,可以灵活地控制 bean 的作用域。
神级现场
在MyBatis中,只需要写个接口就能实现就能运行SQL你不觉得奇怪吗?难道你的接口MyBatis自动帮你实现了?非也,MyBatis并没有去实现你的接口!那么你会问,那为什么直接可以调用接口?这就是FactoryBean+Proxy的神级现场设计,下面为了简答只演示调用接口就实现功能。
学生接口
public interface Student {void say();
}
代理工厂Bean
/*** 这里根本没有Student的实现类,只有一个接口,在代理中实现了接口的方法* @param <T>*/
@Component
public class StudentFactoryBeanProxy<T> implements FactoryBean<T> {/*** 通过 FactoryBean 创建代理对象Bean* @return 一个代理对象* @throws Exception*/@Overridepublic T getObject() throws Exception {// 创建了一个 InvocationHandler 对象,用于处理代理对象的方法调用InvocationHandler handler = (proxy, method, args) -> {String name = method.getName();if ("say".equals(name)) {System.out.println("Hi, I am a good student!");}return "只要拿到接口,就能在代理中实现任何功能!";};// 使用 Proxy.newProxyInstance() 方法创建代理对象,也就是外部的接口其实是一个代理对象return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{Student.class}, handler);}@Overridepublic Class<?> getObjectType() {return Student.class;}
}
配置类
@Configuration
@ComponentScan("com.example.jaytecharchite.factorybeandemo2")
public class MyConfig {
}
测试
public class MainTest {private Logger logger = LoggerFactory.getLogger(MainTest.class);@Autowiredprivate Student student; // 其实这个就是一个代理对象,只不过名字还是Student@Testpublic void test(){AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);logger.info("容器启动完成!");student.say();}
}
输出:
2024-07-03 23:24:36.144 INFO 17080 — [ main] c.e.j.factorybeandemo2.MainTest : 容器启动完成!
Hi, I am a good student!
注意看,我们并没有实现Student哦,但是它调用student.say();就能说话!神级现场!!!
相关文章:
FactoryBean 原理简介
FactoryBean 首先是一个工厂类,它可以生产指定的Bean,特殊之处在于它可以向Spring容器中注册两个Bean,一个是它本身,一个是FactoryBean.getObject()方法返回值所代表的Bean。通过实现 FactoryBean 接口,你可以控制某个…...
Redis中hash类型的操作命令(命令的语法、返回值、时间复杂度、注意事项、操作演示)
文章目录 字符串和哈希类型相比hset 命令hget 命令hexistshdelhkeyshvalshgetallhmgethlenhsetnxhincrbyhincrbyfloat 字符串和哈希类型相比 假设有以下一种场景:现在要在 Redis 中存储一个用户的基本信息(id1、namezhangsan、age17),下图表示使用字符串…...
UE5基本操作(二)
文章目录 前言相机的移动速度修改默认地图使用初学者内容包文件夹结构 总结 前言 在我们的上一篇文章中,我们已经介绍了一些Unreal Engine 5(UE5)的基本操作。UE5是一款强大的游戏开发引擎,它提供了许多工具和功能,使…...
React Navigation 和 Expo Router
React Navigation 是 React Native 社区最常用的导航库,其具有高度可定制性且性能良好的特性。它提供了一系列导航器(如堆栈导航器、标签导航器、抽屉导航器等),可以满足绝大多数的页面导航需求。 Expo Router 是 Expo 官方最新发…...
如何使用python网络爬虫批量获取公共资源数据教程?
原文链接:如何使用python网络爬虫批量获取公共资源数据教程?https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247608240&idx4&snef281f66727afabfaae2066c6e92f792&chksmfa826657cdf5ef41571115328a09b9d34367d8b11415d5a5781dc4c…...
常见位运算总结
1.基础位运算 左移 (<<): 最左侧位不要了, 最右侧补 0 右移(>>): 最右侧位不要了, 最左侧补符号位(正数补0, 负数补1) 按位取反(~):如果该位为 0 则转为 1, 如果该位为 1 则转为…...
自动化任务工具 -- zTasker v1.94 绿色版
软件简介 zTasker 是一款功能强大的自动化任务管理软件,以其简洁易用、一键式操作而著称。软件体积小巧,启动迅速,提供了超过100种任务类型和30多种定时/条件执行方法,能够满足用户在自动化方面的多样化需求。 zTasker 支持定时任…...
mybatis mapper.xml 比较运算符(大于|小于|等于)的写法: 转义和<![CDATA[]]>
文章目录 引言I 使用xml 原生转义的方式进行转义II 使用 <![CDATA[ 内容 ]]>引言 应用场景:查询时间范围 背景:在 *.xml 中使用常规的 < > = <= >= 会与xml的语法存在冲突 <![CDATA[]]> 比 转义符 来的繁琐 <![CDATA[]]> 表示xml解析器忽略…...
UE5的基本操作
涵盖了从建模、快捷键使用、界面操作到性能分析等多个方面,以下是一些关键点和技巧:12 建模操作: 使用Shift5切换到建模模式,可以通过Shapes创建基本图元如立方体、球体等。 利用Create面板中的工具,如polyext自由创…...
C++ 实现学生成绩管理系统
C 实现学生成绩管理系统 思路: 定义 Student 类,包含学生的基本信息和成绩。实现添加学生、删除学生、修改成绩、显示所有学生成绩和查找学生的功能。使用向量(vector)存储学生信息。 #include <iostream> #include <…...
Elasticsearch 第四期:搜索和过滤
序 2024年4月,小组计算建设标签平台,使用ES等工具建了一个demo,由于领导变动关系,项目基本夭折。其实这两年也陆陆续续接触和使用过ES,两年前也看过ES的官网,当时刚毕业半年多,由于历史局限性导…...
力扣1124.表现良好的最长时间段
力扣1124.表现良好的最长时间段 哈希表存最小的下标 当s[i] > 0 那么他到头可以构成一个合法时间段否则 找到之前的 s[i] - 1 的下标: 因为连续的前缀和一定只相差1若想算更小的s[i] - 2,s[i] - 3…一定会先算到s[i] - 1那么这些更小数必然在 s[i]−1 首次出现的…...
算法训练营day67
题目1: #include <iostream> #include <vector> #include <string> #include <unordered_set> #include <unordered_map> #include <queue>using namespace std;int main() {string beginStr, endStr;int n;cin >> n;ci…...
人工智能--图像语义分割
个人主页:欢迎来到 Papicatch的博客 课设专栏 :学生成绩管理系统 专业知识专栏:专业知识 文章目录 🍉引言 🍉介绍 🍈工作原理 🍍数据准备 🍍特征提取 🍍像素分…...
fl studio20和21用哪一个好?FL-Chan from FL Studio欣赏
最近接到很多小伙伴的私信,都在问我平时会使用哪些音乐软件,能不能给一些参考。其实每个人的使用习惯不一样,需求也不一样。以DAW为例,有些人就是喜欢FL Studio,有些人吹爆Studio One,还有些人习惯使用Cuba…...
OpenCV直方图计算函数calcHist的使用
操作系统:ubuntu22.04OpenCV版本:OpenCV4.9IDE:Visual Studio Code编程语言:C11 功能描述 图像的直方图是一种统计表示方法,用于展示图像中不同像素强度(通常是灰度值或色彩强度)出现的频率分布。具体来说…...
09 docker 安装tomcat 详解
目录 一、安装tomcat 1. tomcat镜像的获取 2. docker创建容器实列 3. 访问测试 404错误 4. 解决方案 5. 使用免修改版容器镜像 5.1. 运行实列的创建 5.2. 出现问题及解决: 6. 验证 OK 一、安装tomcat 1. tomcat镜像的获取 docker search tomcat #docker …...
44.实现管理HOOK点的链表对象
上一个内容:43.实现HOOK接管寄存器数据 以 43.实现HOOK接管寄存器数据 它的代码为基础进行修改 首先创建一个类 这里创建的名为HOOKPOINT.h HOOKPOINT.cpp文件里面的内容 #include "pch.h" #include "HOOKPOINT.h"HOOKPOINT::HOOKPOINT() {…...
Unity小知识
1.当我们把摄像机的内容渲染到RenderTexture上而不是屏幕上时,那么相机的Aspect默认会设置成和RenderTexture的分辨率一样.不过最终如果把RenderTexture作为贴图贴到模型上去的时候还是会被UV拉伸和缩小的。 2.要想自定义UnityPackage的内容,只要找到UnityProject/L…...
【Jupyter Notebook与Git完美融合】在Notebook中驾驭版本控制的艺术
标题:【Jupyter Notebook与Git完美融合】在Notebook中驾驭版本控制的艺术 Jupyter Notebook是一个流行的开源Web应用程序,允许用户创建和共享包含实时代码、方程、可视化和解释性文本的文档。而Git是一个广泛使用的分布式版本控制系统,用于跟…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
