Spring框架FactoryBean接口的作用和应用

一、FactoryBean源码解读

FactoryBean<T> 是 Spring 框架 beans.factory包中的一个接口,从字面意思可以理解为工厂bean,它是干什么的,类名上的泛型又是指什么,有什么作用?

注释看不懂没关系,先看一下它有哪些方法。

public interface FactoryBean<T> {// 获取实例@NullableT getObject() throws Exception;// 获取实例的class类型@NullableClass<?> getObjectType();// 实例默认为单例default boolean isSingleton() {return true;}
}

方法看完了,还是不知道它能干嘛,继续看它的子类。

子类真多,一个屏幕放不下,找几个看看。

二、FactoryBean的子类

先看两个简单的实现类:

1、AuthenticationManagerFactoryBean类

AuthenticationManagerFactoryBean是spring-boot-starter-security 框架中的一个类,这个类实现了FactoryBean<AuthenticationManager>, BeanFactoryAware 两个接口的功能。

BeanFactoryAware 接口在《Spring框架Aware接口的作用和应用》一文中已经介绍过,它是用于在当前类初始化时获取 BeanFactory 接口以供当前类使用的。

FactoryBean<AuthenticationManager>的泛型位置放置了一个类AuthenticationManager。

AuthenticationManagerFactoryBean实现了FactoryBean接口中3个方法

在getObject() 方法中创建了AuthenticationManager实例并返回。

在该方法的最后一行代码可以看出,AuthenticationManager的实例是交给ProviderManager 对象管理的。

因此,在getObjectType()方法中返回的是ProviderManager对象的class。

在 isSingleton()方法中返回的依旧是单例模式。

从以上代码的分析,我们可以得出一个结论,AuthenticationManagerFactoryBean类是用来创建AuthenticationManager对象实例的工厂bean类。

2、CronTriggerFactoryBean类

CronTriggerFactoryBean是Spring框架定时任务 org.springframework.scheduling.quartz 包中的一个类,这个类实现了FactoryBean<CronTrigger>, BeanNameAware, InitializingBean 3个接口的功能。

BeanNameAware 是用于在当前类初始化时获取当前类的bean名称的。

InitializingBean 接口是用于重写初始化方法的。

FactoryBean<CronTrigger>的泛型位置放置了一个类CronTrigger。 

CronTrigger是定时任务的表达式对象。 

在重写的 afterPropertiesSet() 初始化方法中,new 了一个 CronTriggerImpl对象并返回。

CronTriggerImpl位于定时任务框架 org.quartz中,它是CronTrigger的实现类。

接下来就是FactoryBean三个方法的实现:

getObject() 方法中直接初始化方法中创建的cronTrigger变量。

getObjectType()方法返回的是CronTrigger的class,它是CronTriggerImpl的超类。

isSingleton()方法中返回的是单例模式。

从以上代码的分析,我们也可以得出一个结论,CronTriggerFactoryBean类是用来创建CronTriggerFactory对象实例的工厂bean类。

再来看两个复杂一些的抽象类。

3、AbstractFactoryBean<T>抽象类

AbstractFactoryBean<T>实现了FactoryBean<T>, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean 5个接口。

FactoryBean<T>泛型位置的参数还是泛型。

BeanClassLoaderAware接口是用于在当前类初始化时获取类加载对象ClassLoader的。

BeanFactoryAware接口是用于在当前类初始化时获取BeanFactory的。

InitializingBean 接口是用于重写初始化方法的。

DisposableBean 接口是用于重写销毁方法的。

在初始化方法中,如果是单例则进行实例化:


protected abstract T createInstance() throws Exception;
@Override
@Nullable
public abstract Class<?> getObjectType();
@Override
public final T getObject() throws Exception {if (isSingleton()) {return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());}else {return createInstance();}
}

实例化是个抽象方法,由其子类实现具体实例化逻辑。

getObjectType()也是抽象方法,由其子类实现。

getObject()是个final方法,子类不能更改,如果是单例则返回单例实例,否则每次调用返回一个新的实例。

在AbstractFactoryBean抽象类中,我们最关心的FactoryBean中的2个方法,一个由其子类实现,一个由抽象类实现。不管是子类还是抽象类,目的都是一样的。

4、ListFactoryBean类

ListFactoryBean是AbstractFactoryBean<List<Object>>的子类,通过这个头可以看出它是创建一个List集合的。

在getObjectType()方法中,直接返回List集合的class。

在createInstance()方法中,实现了List集合的实例化逻辑。

还有很多抽象类、实现类,我们就不举例了。

从以上3个类我们可以推断出,FactoryBean就是一个创建和管理bean实例的特殊工厂bean。

FactoryBean的子类很多,但是子接口只有一个,我们也看下。

三、SmartFactoryBean子接口和应用

1、SmartFactoryBean源码解读

SmartFactoryBean接口扩展了FactoryBean接口


public interface SmartFactoryBean<T> extends FactoryBean<T> {// 是否为原型,默认falsedefault boolean isPrototype() {return false;}// 是否提前初始化,默认falsedefault boolean isEagerInit() {return false;}
}

这个接口增加了两个方法:

isPrototype()方法,判断当前要创建的bean实例是否是原型。

isEagerInit()方法,判断当前要创建的bean实例是否提前实例化。

2、FactoryBean和SmartFactoryBean的应用比较

1)定义一个bean


public class MyBean2{public MyBean2() {System.out.println("1、实例化 MyBean2");}public void fun(){System.out.println("2、调用  MyBean2 的 fun 方法");}
}

2)把MyBean2对象封装到MyBean2FactoryBean中


@Component
public class MyBean2FactoryBean implements SmartFactoryBean<MyBean2>{private MyBean2 myBean2;@Overridepublic MyBean2 getObject() throws Exception {if(myBean2 == null){myBean2 = new MyBean2();}return myBean2;}@Overridepublic Class<?> getObjectType() {return MyBean2.class;}@Overridepublic boolean isEagerInit() {return true;}
}

测试FactoryBean接口时,可以把SmartFactoryBean替换成FactoryBean,并把isEagerInit注释掉。

3)测试代码

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
/*** SmartFactoryBean接口演示代码,是否提前实例化的验证* @author * @Description**/
public class SpringApp5 {public static void main(String[] args) {   AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyBean2FactoryBean.class);MyBean2FactoryBean myBean2FactoryBean = context.getBean(MyBean2FactoryBean.class);try {System.out.println("还没有调用呢");MyBean2 myBean = myBean2FactoryBean.getObject();myBean.fun();} catch (Exception e) {e.printStackTrace();}context.close();}
}

当我们使用 FactoryBean 的时候,Spring 在实例化 Bean 的时候,实例化的是MyBean2FactoryBean 工厂 Bean,MyBean2则是在工厂 Bean中实例化的。

当把工厂bean更改为FactoryBean的时候,运行结果如下:

Creating shared instance of singleton bean 'myBean2FactoryBean'
还没有调用呢
1、实例化 MyBean2
4、调用  MyBean2 的 fun 方法

当把工厂bean更改为SmartFactoryBean的时候,并设置提前实例化时,运行结果如下:

Creating shared instance of singleton bean 'myBean2FactoryBean'1、实例化 MyBean2还没有调用呢4、调用  MyBean2 的 fun 方法

当我们把 isEagerInit() 返回 true时,实例化提前了,没有使用就被实例化了。

最后总结

FactoryBean<T>是一个特殊的bean,用于创建和管理自定义 Bean 实例的工厂bean,其中类名中的泛型就是要创建Bean的类名,如果子类是一个抽象类,那么具体的创建实例过程会延伸到其子类。

FactoryBean<T>定义了一种创建 Bean 的方式,允许在 Bean 的创建过程中进行更多的自定义操作。通过实现 FactoryBean 接口,我们可以创建复杂的 Bean 实例,或者在 Bean 实例化之前进行一些额外的逻辑处理,比如重写初始化方法的InitializingBean等等。

在看源码时,如果看到某个类名是:XxxxFactoryBean,我们就能在瞬间知道这个类是创建Xxxx实例的工厂bean。如果还带有泛型XxxxFactoryBean<T>,就说明这是一个抽象类,具体的创建实例的过程在其子类中。

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

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

相关文章

阿里巴巴向国际用户开放人工智能模型平台ModelScope(魔搭社区)

阿里巴巴对 Hugging Face 和 Amazon Bedrock 的回应包含 5,000 多个中国专业模型&#xff0c;以及 1,500 个工具包和数据集 阿里云已将其人工智能模型存储库ModelScope&#xff08;魔搭社区&#xff09;的访问权限扩展至全球英语用户&#xff0c;意在吸引更多国际企业和开发者…

自动建立用户练习

一丶编辑文本存放用户名 vim userlist 二丶编辑文本存放需要创建用户的密码 vim passlist 三丶编辑脚本 vim create_user.sh #!bin/bash [ "$#" -lt "2" ] && { #echo error please input userlist anpassli…

高德.js2.0绘制多条折线(轨迹)及清除所有折线

2.0版本的地图,需要绘制多条折线的时候,就需要循环生成,因此也需要循环清除 for (let j 0; j < combinedArray.length; j) {const item combinedArray[j];this.polyline new AMap.Polyline({map: this.map,path: item,showDir: true,strokeColor: "#28F", //线…

简过网:上万元的学费,考公到底要不要报个培训班?

考公报不报班一直是很多朋友比较纠结一件事&#xff0c;报班了学费太贵&#xff0c;不报班又怕考不上&#xff0c;如果你也有这种困扰&#xff0c;那么&#xff0c;不妨看看这篇文章&#xff01; 首先&#xff0c;对于报班VS自学这个问题&#xff0c;小编的建议是&#xff1a;…

HTML【介绍】

HTML【介绍】 一、Web认知 1.网页组成 文字、图片、音频、视频、超链接 2.五大浏览器 IE浏览器、火狐浏览器&#xff08;Firefox&#xff09;、谷歌浏览器&#xff08;Chrome&#xff09;、Safari浏览器、欧朋浏览器&#xff08;Opera&#xff09; 3.Web标准的构成 HTML…

GPU_Gems-物理模型的水模拟

创建一个多网格的平面 void GraphicsWindowBase::RenderPlane() {constexpr int width 150;constexpr int depth 150;constexpr int vertNum width * depth;float length 60.f;if (quadVAO 0){float planeVert[vertNum * 5];float offsetX length / (width - 1.f);float…

朴素贝叶斯机器学习算法:从基础到高级

文章目录 一、说明二、从一个简单例子入手2.1 简单示例2.2 朴素贝叶斯算法的直觉解释 三、在训练阶段&#xff0c;朴素贝叶斯内部会发生什么&#xff1f;3.1 朴素贝叶斯如何处理数值数据&#xff1f;3.2 如果数据分布不是高斯分布怎么办&#xff1f;3.3 朴素贝叶斯的数值稳定性…

Flutter Navigator.popUntil 参数传递

Flutter 使用页面传参 以下是 在flutter 中页面传参的常用形式&#xff0c;都可以有有直接的传值参数提供。 Navigator.push #跳转到指定页面 压栈路由表Navigator.pushReplacement #关闭当前页面 跳转到指定页面压栈路由表Navigator.pus…

JavaScript的学习之自增自减

目录 一、自增 第一种&#xff1a;a 第二种&#xff1a;a 二、自减 一、自增 定义&#xff1a;可以是变量在自身的基础上增加1 自增分为两种&#xff1a;后&#xff08;a&#xff09;和前&#xff08;a&#xff09; 无论是a和a都会立即使原变量的值增1&#xff0c;不同的使…

【windows】电脑如何关闭Bitlocker硬盘锁

如果你的硬盘显示这样的一把锁&#xff0c;说明开启了Bitlocker硬盘加密。 Bitlocker硬盘锁&#xff0c;可以保护硬盘被盗&#xff0c;加密防止打开查看数据。 方法一&#xff1a;进入“控制面板->BitLocker 驱动器加密”进行设置。或者“控制面板\系统和安全->BitLocke…

排序算法系列二:归并排序、快速排序

零、说在前面 本文是一个系列&#xff0c; 入口请移步这里 一、理论部分 1.4&#xff1a;归并排序 1.4.1&#xff1a;算法解读&#xff1a; 使用二分法和插入排序两种算法的思想来实现。流程分为“拆分”、“合并”两大部分&#xff0c;前者就是普通的二分思想&#xff0c;将…

电商平台数据功能封装API需要注意些什么?如何调用封装后的API?

一、引言 随着电商行业的蓬勃发展&#xff0c;电商平台的数据功能愈发复杂多样&#xff0c;如何高效、安全地管理和使用这些数据成为了电商平台开发者面临的重要问题。API&#xff08;Application Programming Interface&#xff09;作为不同软件之间进行通信的桥梁&#xff0…

预警与校准并行:可燃气体报警器在矿区井下甲烷泄露防控中的应用

在矿区井下作业中&#xff0c;甲烷泄露是一个严重威胁工人生命安全和矿区生产安全的隐患。因此&#xff0c;及时、准确地预警甲烷泄露并采取相应的处理措施显得尤为重要。 可燃气体报警器作为一种有效的监测工具&#xff0c;在预防甲烷泄露事故中发挥着不可替代的作用。 在这…

STM32HAL库 -- RS485 开发板通信(速记版)

在本章中&#xff0c; 我们将使用 STM32F429的串口 2 来实现两块开发板之间的 485 通信(半双工)。 RS485 简介 485&#xff08;一般称作 RS485/EIA-485&#xff09;隶属于 OSI 模型物理层&#xff0c;是串行通讯的一种。电气特性规定为 2 线&#xff0c;半双工&#xff0c;多…

CVPR 2024最佳论文分享:文本到图像生成的丰富人类反馈

CVPR&#xff08;Conference on Computer Vision and Pattern Recognition&#xff09;是计算机视觉领域最有影响力的会议之一&#xff0c;主要方向包括图像和视频处理、目标检测与识别、三维视觉等。近期&#xff0c;CVPR 2024公布了最佳论文。共有10篇论文获奖&#xff0c;其…

如何实现智慧农田的精准灌溉

如何实现智慧农田的精准灌溉 智慧农田的精准灌溉是现代农业技术发展的重要组成部分&#xff0c;它集成了物联网、大数据分析、人工智能以及现代水利技术&#xff0c;旨在通过实时监测土壤湿度、气象条件及作物生长状况&#xff0c;实现水资源的高效利用和作物产量、品质的双重…

解决2021版IDEA新建没有Server问题

2024-06-27可用 我是这样解决的&#xff0c;仅供参考 IDEA软件是2021.1.1版 导入Tomcat的Servlet包&#xff0c;就解决了&#xff0c;详见下图操作 1. 打开项目结构 2. 选Libraries 3. 找到Tomcat安装路径 比如我安装在了“C:\soft”目录&#xff0c;就去这个目录找 然后记得…

基于Cardinal的AWD攻防平台搭建与使用以及基于docker的题目环境部署

关于 CTF 靶场的搭建与完善勇师傅前面已经总结过了&#xff0c;参考&#xff1a; CTF靶场搭建及Web赛题制作与终端docker环境部署_ctfoj搭建-CSDN博客 基于H1ve一分钟搭好CTF靶场-CSDN博客 Nginx首页修改及使用Nginx实现端口转发_nginx 修改欢迎首页-CSDN博客 关于H1ve导…

cPanel的SSL证书续订方法

在现代互联网环境中&#xff0c;SSL证书对于保障网站的安全和数据加密至关重要。下面我们将介绍如何在cPanel中续订SSL证书&#xff0c;并适时结合Hostease服务器的优势&#xff0c;帮助您更好地理解和操作。 生成私钥和CSR&#xff08;证书签名请求&#xff09; 难易程度&am…

Unity中模拟抛物线(非Unity物理)

Unity中模拟抛物线非Unity物理 介绍剖析问题以及所需公式重力加速度公式&#xff1a;h 1/2*g*t*t(h 1/2 * g * t ^ 2)速度公式&#xff1a;Vt V初 a * t 主要代码总结 介绍 用Unity物理系统去做的抛物线想要控制速度或者想要细微的控制一些情况是非常困难的。所以想要脱离U…