FactoryBean 原理简介

FactoryBean 首先是一个工厂类,它可以生产指定的Bean,特殊之处在于它可以向Spring容器中注册两个Bean,一个是它本身,一个是FactoryBean.getObject()方法返回值所代表的Bean。通过实现 FactoryBean 接口,你可以控制某个 Bean 的实例化过程,提供比默认机制更复杂的创建逻辑。

FactoryBean接口

public interface FactoryBean<T> {
    @Nullable
    T getObject() throws Exception;

    @Nullable
    Class<?> 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 {
    @Override
    public Object getObject() throws Exception {
        return new Student();
    }
    @Override
    public Class<?> getObjectType() {
        return Student.class;
    }

    @Override
    public 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);
    @Test
    public 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
     */
    @Override
    public 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);
    }

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

配置类

@Configuration
@ComponentScan("com.example.jaytecharchite.factorybeandemo2")
public class MyConfig {
}

测试

public class MainTest {
    private Logger logger = LoggerFactory.getLogger(MainTest.class);
    @Autowired
    private Student student; // 其实这个就是一个代理对象,只不过名字还是Student
    @Test
    public 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();就能说话!神级现场!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【APK】SDKManager运行后闪退

本地JDK已安装&#xff0c;且配置了环境变量&#xff0c;未安装 android studiio 问题描述&#xff1a;右键以管理员身份运行 SDKManager&#xff0c;终端窗口闪退 问题原因&#xff1a;未找到正确的Java路径 解决办法&#xff1a; 1.修改tools目录下的 android.bat 文件&am…

数字人直播源码开发全攻略揭秘:如何搭建自己的数字人直播平台?

当前&#xff0c;数字人直播逐渐成为众多中小型企业线上带货和品牌宣传的不二之选&#xff0c;而艾媒研究数据也显示&#xff0c;超五成以上的被调查群体的企业使用过虚拟人技术&#xff0c;超三成被调查群体的企业计划使用虚拟人技术。在此背景下&#xff0c;越来越多的创业者…

10计算机视觉—物体检测算法

目录 1.R-CNN(区域卷积神经网络)2014兴趣区域(RoI)池化层Fast RCNN 2015Faster R-CNN 2015Mask R-CNN 2017总结2. SSD(单发多框检测)2016SSD模型总结3.YOLO(你只看一次)快!很重要4.目标检测算法性能对比5.SSD代码实现 使用很少,比不上yolo多尺度锚框实现SSD代码实现训练…

DOM 中包含哪些重要方法

1. alert 带有指定消息的警告框 alert("hello world"); 2. confirm 带有确定和取消的对话框&#xff0c;点击确定返回 true&#xff0c;点击取消返回 false confirm("你好吗"); 3. prompt 显示一个提示框&#xff0c;允许用户输入文本&#xff0c;点击…

数据恢复篇:5 款最佳 Mac 数据恢复软件

说到保护我们的数字生活&#xff0c;数据恢复软件的重要性怎么强调都不为过。无论您是意外删除了假期照片的普通用户&#xff0c;还是面临硬盘损坏的专业人士&#xff0c;随之而来的恐慌都是普遍存在的。幸运的是&#xff0c;数据恢复工具可以缓解这些压力。在Mac用户可用的众多…

零障碍入门:SSH免密登录与Hadoop生态系统的完美搭档【实训Day02】

一、 SSH免密登录配置 1 生成公钥和秘钥(在hadoop101上) # su star # cd /home/star/.ssh # ssh-keygen -t rsa 2 公钥和私钥 公钥id_rsa.pub 私钥id_rsa 3 将公钥拷贝到目标机器上(在hadoop101上) # ssh-copy-id hadoop101 # ssh-copy-id hadoop102 # ssh-co…

翔云发票查验接口状态码说明,哪种情况扣次数那种情况不扣次数呢

翔云发票查验API&#xff0c;实时联网&#xff0c;可以实现发票信息真伪的快速核验&#xff0c;帮助企业财务摆脱繁琐的发票真伪查验工作。那么知道了发票查验接口的作用&#xff0c;对于开发者而言&#xff0c;接口返回的状态码又分别代表什么含义呢&#xff1f;下面就翔云发票…

【Elasticsearch】Elasticsearch索引创建与管理详解

文章目录 &#x1f4d1;引言一、Elasticsearch 索引的基础概念二、创建索引2.1 使用默认设置创建索引2.2 自定义设置创建索引2.3 创建索引并设置映射 三、索引模板3.1 创建索引模板3.2 使用索引模板创建索引 四、管理索引4.1 查看索引4.2 更新索引设置4.3 删除索引 五、索引别名…

掌握高效实用的VS调试技巧

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 1.编程常见的错误 1.1编译型错误 编程编译型错误是指在编译代码时发现的错误。编译器在编译过程中会检查代码是否符合语法规范和语义要求&#xff0c;如果发现错误会产生编译错误。 直接看错误提示信息&#xff08;双击&#…

超声波气象站的工作原理

TH-CQX5超声波气象站中的超声波技术是其核心工作原理之一&#xff0c;以下是关于超声波气象站中超声波的详细解释&#xff1a;超声波是一种频率高于人耳能听到的声音频率范围的声波&#xff0c;通常指频率在20kHz以上的声波。超声波具有较短的波长和强的穿透能力&#xff0c;能…

相机,手机,行车记录仪及监控视频修复软件: Stellar Repair for Video

天津鸿萌科贸发展有限公司是 Stellar 系列数据恢复软件的授权代理商。 Stellar Repair for Video 是一款强大的工具&#xff0c;用于修复从主流相机品牌&#xff08;如佳能、尼康、索尼&#xff09;、行车记录仪、监控录像机、手机和其他视频设备拍摄的无法访问和损坏的视频。…

zabbix 配置企业微信告警

1、申请一个企业微信&#xff0c; 官网链接 2、群内申请一个机器人 下载电脑版企业微信&#xff0c;登录后&#xff0c;在要接收群消息的群里&#xff0c;点击右上角三个点&#xff0c;添加机器人后&#xff0c;保存机器人的webhook地址 上传应用logo&#xff0c;填写应用名称…

MySQL—创建和修改数据表结构

创建表 实例&#xff1a; CREATE TABLE user (id INT,name VARCHAR(255),password VARCHAR(255),birthday DATE) CHARACTER SET utf8 COLLATE utf8_bin ENGINE INNODB; 显示数据库中的表 show tables from hsp; 显示表结构 desc dept; 修改表 实例&#xff1a; 代码&…

Vue85-Vuex的求和案例

一、需求 二、开发 2-1、index.js中vuex的代码 注意&#xff1a; 书写格式&#xff1a;actions中的函数名用小写&#xff01;mutations中的函数名&#xff0c;用大写。 注意&#xff1a; 2-2、组件count.vue中的代码 2-3、代码优化 三、actions中的context参数 此写法的后…

网安小贴士(6)TCP/IP分层

一、前言 1983年&#xff0c;美国国防部决定将TCP/IP作为所有计算机网络的标准协议&#xff0c;这标志着TCP/IP正式成为互联网的基础协议。随着个人计算机的普及和网络技术的发展&#xff0c;TCP/IP模型被广泛应用于各种网络环境中&#xff0c;包括局域网&#xff08;LAN&#…

天行健咨询|六西格玛绿带培训是投资未来,还是金钱的“黑洞”?

六西格玛绿带培训&#xff0c;作为一种被众多企业推崇的培训课程&#xff0c;自然成为了众多职场人士关注的焦点。然而&#xff0c;面对培训的高昂费用和时间成本&#xff0c;很多人开始质疑&#xff1a;参加六西格玛绿带培训&#xff0c;到底是投资还是浪费钱&#xff1f;深圳…

前端重点之:Vue+websocket通信详细用法和websocket心跳机制的使用,websocket断开实时监测,websocket实时通信

今年年初找工作,好多gou面试官总喜欢问关于websocket通信的使用方式,此次又用到了,在此做个总结:主要包含websocket的具体使用方法,和重点:(心跳机制的使用),就是主要是前端实时监测websocket是否有断连和数据的处理 在前端开发中,WebSocket 是一种常见的技术,用于…

安华金和—可信数据空间助力公共数据授权运营安全有序开展的实践探索

伴随数字化、网络化和智能化的快速发展&#xff0c;数字经济与实体经济深度融合&#xff0c;数据已然成为经济发展赖以依托的基础性、战略性资源&#xff0c;对社会生产、分配、流通、消费和社会服务管理等各环节产生深刻影响。我国高度重视数字经济发展&#xff0c;将数据列入…

构造函数深入理解

目录 构造函数构造函数体赋值初始化列表初始化列表格式初始化列表的意义以及注意点const修饰的成员变量初始化对象成员具体初始化的地方缺省值存在的意义例子1例子2 初始化与赋值引用成员变量的初始化注意点1注意点2我的疑惑 自定义类型成员初始化例子1例子2例子3例子4 初始化列…

Sentinel链路流控模式失效的解决方法

解决方法 1、在pom.xml中增加sentinel-web-servlet的依赖&#xff0c;我使用的版本是1.7.1 <dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-web-servlet</artifactId> </dependency>2、在项目中添加一个FilterCon…