当前位置: 首页 > news >正文

Spring 中 BeanFactory 和 FactoryBean 有何区别?

这也是 Spring 面试时一道经典的面试问题,今天我们来聊一聊这个话题。

其实从名字上就能看出来个一二,BeanFactory 是 Factory 而 FactoryBean 是一个 Bean,我们先来看下总结:

  • BeanFactory 是 Spring 框架的核心接口之一,用于管理和获取应用程序中的 Bean 实例。它是一个工厂模式的实现,负责创建、配置和管理 Bean 对象。BeanFactory 是 Spring IoC 容器的基础,它可以从配置元数据(如 XML 文件)中读取 Bean 的定义,并在需要时实例化和提供这些 Bean。

  • FactoryBean 是一个特殊的 Bean,它是一个工厂对象,用于创建和管理其他 Bean 的实例。FactoryBean 接口定义了一种创建 Bean 的方式,它允许开发人员在 Bean 的创建过程中进行更多的自定义操作。通过实现 FactoryBean 接口,开发人员可以创建复杂的 Bean 实例,或者在 Bean 实例化之前进行一些额外的逻辑处理。

区别在于,BeanFactory 是 Spring 框架的核心接口,用于管理和提供 Bean 实例,而 FactoryBean 是一个特殊的 Bean,用于创建和管理其他 Bean 的实例。FactoryBean 在 Bean 的创建过程中提供更多的自定义能力,允许进行额外的逻辑处理。

可能有的小伙伴看的还不是很清楚,我们再来详细看下。

1. BeanFactory

BeanFactory 看名字就知道这是一个 Bean 工厂,小伙伴们知道,Spring IoC 容器帮我们完成了 Bean 的创建、管理等操作,那么这些操作都离不开 BeanFactory。

我们来简单看下 BeanFactory 的代码:

public interface BeanFactory {String FACTORY_BEAN_PREFIX = "&";Object getBean(String name) throws BeansException;<T> T getBean(String name, Class<T> requiredType) throws BeansException;Object getBean(String name, Object... args) throws BeansException;<T> T getBean(Class<T> requiredType) throws BeansException;<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);boolean containsBean(String name);boolean isSingleton(String name) throws NoSuchBeanDefinitionException;boolean isPrototype(String name) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;@NullableClass<?> getType(String name) throws NoSuchBeanDefinitionException;@NullableClass<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;String[] getAliases(String name);}

这些方法基本上都见名知义:

  • FACTORY_BEAN_PREFIX:这个变量其实是说,如果当前 Bean 不是像 User、Book 这种普通 Bean,而是一个 FactoryBean,就给这个 Bean 名字加一个 & 前缀,这个我在第二小节和小伙伴们演示。

  • getBean:这个方法就是根据 Bean 的名字、类型等去查询 Bean。

  • getBeanProvider:这个方法可以获取一个 ObjectProvider,ObjectProvider 是 Spring 框架中的一个接口,用于获取 Bean 对象的实例。它提供了一种延迟加载 Bean 的方式,可以在需要时动态地获取 Bean 实例(懒加载)。

  • containsBean:判断是否包含某个 Bean。

  • isSingleton:判断某个 Bean 是否是单例的。

  • isPrototype:判断某个 Bean 是否是多例的。

  • isTypeMatch:判断某一个 Bean 的类型是否是给定类型。

  • getType:获取 Bean 的类型。

  • getAliases:获取 Bean 的别名。

可以看到,很多都是大家日常开发中常见常用的方法。

很多小伙伴刚开始接触 Spring 的时候,都会用到一个对象 ClassPathXmlApplicationContext,这其实就是 BeanFactory 的一个子类。我们来看下 BeanFactory 的继承图:

图片

继承类比较多,我说几个大家可能比较熟悉的:

  1. ClassPathXmlApplicationContext:这个是 Spring 容器启动时,从当前类路径下去加载 XML 配置文件,参数就是 classpath 下 XML 的文件路径。

  2. FileSystemXmlApplicationContext:这个是 Spring 容器启动时,从文件系统中去加载 XML 配置文件,参数一个绝对路径。

  3. AnnotationConfigApplicationContext:这个是如果我们使用 Java 代码去做 Spring 容器的配置的话,通过这个配置类去加载 Java 配置类。

  4. DefaultListableBeanFactory:这个默认实现了 ListableBeanFactory 和 BeanDefinitionRegistry 接口,是一个比较成熟的 BeanFactory。

好啦,这就是 BeanFactory 的特点,大家明白了吧~

2. FactoryBean

2.1 分析

FactoryBean 其实很多小伙伴可能都见过,只是可能没去总结归纳。我给小伙伴们举几个例子。

在 SSM 项目中,如果我们要配置 MyBatis 到项目中,一般需要配置下面这个 Bean:

<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="typeAliasesPackage" value="org.javaboy.shirodemo.model"/><property name="mapperLocations"><list><value>classpath*:org/javaboy/shirodemo/mapper/*.xml</value></list></property>
</bean>

我们在配置 Shiro 的时候,一般都要配置如下 Bean:

<bean class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" id="shiroFilter"><property name="securityManager" ref="securityManager"/><property name="loginUrl" value="/login"/><property name="successUrl" value="/index"/><property name="unauthorizedUrl" value="/unauthorizedUrl"/><property name="filterChainDefinitions"><value>/index=anon/doLogin=anon/hello=user/**=authc</value></property>
</bean>

如果我们前端传递的参数是 key-value 格式,并且有一个日期,那么小伙伴们知道,服务端 SpringMVC 默认无法处理这个日期,需要配置一个日期转换器,一般我们在 Spring 容器中添加如下 Bean:

<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="conversionService"><property name="converters"><set><ref bean="myDateConverter"/></set></property>
</bean>
<mvc:annotation-driven conversion-service="conversionService"/>

小伙伴们观察上面三个 Bean 有一个共同的特点,那就是 Bean 的名字都是 xxxFactoryBean。

为什么要用 xxxFactoryBean 而不直接把需要的 Bean 注入到 Spring 容器中去呢?以 MyBatis 为例:

手动配置过 MyBatis 的小伙伴应该都知道,MyBatis 有两个重要的类,一个是 SqlSessionFactory,还有一个是 SqlSession,通过 SqlSessionFactory 可以获取到一个 SqlSession。但是不知道小伙伴们是否还记得配置代码,手动配置代码如下

public class SqlSessionFactoryUtils {private static SqlSessionFactory SQLSESSIONFACTORY = null;public static SqlSessionFactory getInstance() {if (SQLSESSIONFACTORY == null) {try {SQLSESSIONFACTORY = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));} catch (IOException e) {e.printStackTrace();}}return SQLSESSIONFACTORY;}
}
public class Main {public static void main(String[] args) {SqlSessionFactory factory = SqlSessionFactoryUtils.getInstance();SqlSession sqlSession = factory.openSession();List<User> list = sqlSession.selectList("org.javaboy.mybatis01.mapper.UserMapper.getAllUser");for (User user : list) {System.out.println("user = " + user);}sqlSession.close();}
}

小伙伴们看到,无论是 SqlSessionFactory 还是 SqlSession,都不是正经 new 出来的,其实这两个都是接口,显然不可能 new 出来,前者通过建造者模式去配置各种属性,最后生成一个 SqlSessionFactory 的实例,后者则通过前者这个工厂去生成,最终拿到的都是这两个接口的子类的对象。

所以,对于 SqlSessionFactory 和 SqlSession 就没法在 Spring 容器中直接进行配置,那么对于这样的 Bean,就可以通过 xxxFactoryBean 来进行配置。

我们来看下 SqlSessionFactoryBean 类,源码很长,我挑了重要的出来:

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {private SqlSessionFactory sqlSessionFactory;@Overridepublic SqlSessionFactory getObject() throws Exception {if (this.sqlSessionFactory == null) {afterPropertiesSet();}return this.sqlSessionFactory;}@Overridepublic Class<? extends SqlSessionFactory> getObjectType() {return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();}@Overridepublic boolean isSingleton() {return true;}
}

大家看一下,SqlSessionFactoryBean 需要实现 FactoryBean 接口,并且在实现接口的时候指定泛型是 SqlSessionFactory,也就是 SqlSessionFactoryBean 最终产出的 Bean 是 SqlSessionFactory。实现了 FactoryBean 接口之后,就需要实现接口中的三个方法:

  • getObject:这个方法返回的对象,就是真正要注册到 Spring 容器中的对象,在这个方法中,我们就可以按照各种方式对 Bean 进行各种配置了。

  • getObjectType:这个方法返回注册到 Spring 容器中的对象类型。

  • isSingleton:这个方法用来返回注册到 Spring 容器中的 Bean 是否是单例的。

这就是 FactoryBean 的特点,由于某一个 Bean 的初始化过于复杂,那么就可以通过 FactoryBean 来帮助注册到 Spring 容器中去。

2.2 实践

松哥再写一个简单的例子给小伙伴们体验一把 FactoryBean。

假设我有如下类:

public class Author {private String name;private Integer age;private Author() {}public static Author init(String name, Integer age) {Author author = new Author();author.setAge(age);author.setName(name);return author;}//省略 getter/setter/toString
}

这个类的特点就是构造方法是私有的,你没法从外面去 new,现在我想将这个类的对象注册到 Spring 容器中,那么我可以提供一个 AuthorFactoryBean:

public class AuthorFactoryBean implements FactoryBean<Author> {@Overridepublic Author getObject() throws Exception {return Author.init("javaboy", 99);}@Overridepublic Class<?> getObjectType() {return Author.class;}@Overridepublic boolean isSingleton() {return true;}
}

然后在 Spring 容器中配置 AuthorFactoryBean 即可:

<bean class="org.javaboy.bean.AuthorFactoryBean" id="author"/>

接下来我们就可以从容器中去获取 Author 对象了,但是要注意,通过 author 这个名字拿到的是 Author 对象,而不是 AuthorFactoryBean 对象,如果想要获取到 AuthorFactoryBean 对象,那么要通过 &author 这个名字去获取。

public class Main {public static void main(String[] args) {ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");Object author = ctx.getBean("author");Object authorFactoryBean = ctx.getBean("&author");System.out.println("author.getClass() = " + author.getClass());System.out.println("authorFactoryBean.getClass() = " + authorFactoryBean.getClass());}
}

来看下最终运行结果:

图片

跟我们所想的一致吧~

3. 小结

经过前面的介绍,相信小伙伴们已经能够区分 BeanFactory 和 FactoryBean 了,再来回顾一下本文开头的内容:

  • BeanFactory 是 Spring 框架的核心接口之一,用于管理和获取应用程序中的 Bean 实例。它是一个工厂模式的实现,负责创建、配置和管理 Bean 对象。BeanFactory 是 Spring IoC 容器的基础,它可以从配置元数据(如 XML 文件)中读取 Bean 的定义,并在需要时实例化和提供这些 Bean。

  • FactoryBean 是一个特殊的 Bean,它是一个工厂对象,用于创建和管理其他 Bean 的实例。FactoryBean 接口定义了一种创建 Bean 的方式,它允许开发人员在 Bean 的创建过程中进行更多的自定义操作。通过实现 FactoryBean 接口,开发人员可以创建复杂的 Bean 实例,或者在 Bean 实例化之前进行一些额外的逻辑处理。

区别在于,BeanFactory 是 Spring 框架的核心接口,用于管理和提供 Bean 实例,而 FactoryBean 是一个特殊的 Bean,用于创建和管理其他 Bean 的实例。FactoryBean 在 Bean 的创建过程中提供更多的自定义能力,允许进行额外的逻辑处理。

相关文章:

Spring 中 BeanFactory 和 FactoryBean 有何区别?

这也是 Spring 面试时一道经典的面试问题&#xff0c;今天我们来聊一聊这个话题。 其实从名字上就能看出来个一二&#xff0c;BeanFactory 是 Factory 而 FactoryBean 是一个 Bean&#xff0c;我们先来看下总结&#xff1a; BeanFactory 是 Spring 框架的核心接口之一&#xf…...

黑马程序员项目-黑马点评

黑马点评1 短信登录 基于Session实现登录流程 发送验证码&#xff1a; 用户在提交手机号后&#xff0c;会校验手机号是否合法&#xff0c;如果不合法&#xff0c;则要求用户重新输入手机号 如果手机号合法&#xff0c;后台此时生成对应的验证码&#xff0c;同时将验证码进行…...

ubuntu 20.04 + Anaconda + cuda-11.8 + opencv-4.8.0(cuda)

环境&#xff1a;一键编译opencv-4.8.0(cuda)&#xff0c;前提是已经安装好了cuda和cudnn Anaconda安装 参考: https://blog.csdn.net/weixin_46947765/article/details/130980957 opencv4.8.0编译安装 一键编译shell脚本 VERSION4.8.0test -e ${VERSION}.zip || wget http…...

Linux 目录

目录 1. Linux 目录1.1. 目录 /usr/bin 和 /usr/local/bin 区别 1. Linux 目录 1.1. 目录 /usr/bin 和 /usr/local/bin 区别 /usr/bin 下面的都是系统预装的可执行程序, 系统升级有可能会被覆盖。/usr/local/bin 目录是给用户放置自己的可执行程序。...

Linux shell编程学习笔记21:用select in循环语句打造菜单

一、select in循环语句的功能 Linux shell脚本编程提供了select in语句&#xff0c;这是 Shell 独有的一种循环语句&#xff0c;非常适合终端&#xff08;Terminal&#xff09;这样的交互场景&#xff0c;它可以根据用户的设置显示出带编号的菜单&#xff0c;用户通过输入不同…...

线性回归与线性拟合的原理、推导与算法实现

关于回归和拟合&#xff0c;从它们的求解过程以及结果来看&#xff0c;两者似乎没有太大差别&#xff0c;事实也的确如此。从本质上说&#xff0c;回归属于数理统计问题&#xff0c;研究解释变量与响应变量之间的关系以及相关性等问题。而拟合是把平面的一系列点&#xff0c;用…...

【C++】set和multiset

文章目录 关联式容器键值对一、set介绍二、set的使用multiset 关联式容器 STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、forward_list(C11)等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元…...

二十、泛型(1)

本章概要 基本概念 与 C 的比较 简单泛型 一个元组类库一个堆栈类RandomList 基本概念 普通的类和方法只能使用特定的类型&#xff1a;基本数据类型或类类型。如果编写的代码需要应用于多种类型&#xff0c;这种严苛的限制对代码的束缚就会很大。 多态是一种面向对象思想的泛…...

【Unity数据交互】游戏中常用到的Json序列化

ˊˊ &#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1…...

TCP的滑动窗口和拥塞控制

目录 滑动窗口 1.发送窗口和接收窗口 2.滑动窗口的分类 停止等待协议&#xff1a;发送窗口大小 1&#xff0c; 接收窗口大小 1 后退N帧协议&#xff08;GBN&#xff09;&#xff1a;发送窗口大小 > 1&#xff0c;接收窗口大小 1 选择重传协议&#xff08;SR&#xf…...

零信任网络:一种全新的网络安全架构

随着网络技术的不断发展&#xff0c;网络安全问题日益凸显。传统的网络安全策略往往基于信任和验证&#xff0c;但这种信任策略存在一定的局限性。为了解决这一问题&#xff0c;零信任网络作为一种全新的网络安全架构&#xff0c;逐渐受到人们的关注。本文将对零信任网络的概念…...

基于单片机的智能拐杖软件设计

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式 文章目录 概要 一、整体设计方案2.1本设计设计原理2.1.1单片机基本介绍 二、本设计方案选择三、软件设计AD原理图&#xff1a;原理图…...

小程序如何设置自动预约快递

小程序通过设置自动预约功能&#xff0c;可以实现自动将订单信息发送给快递公司&#xff0c;快递公司可以自动上门取件。下面具体介绍如何设置。 在小程序管理员后台->配送设置处&#xff0c;选择首选配送公司。为了能够支持自动预约快递&#xff0c;请选择正常的快递公司&…...

STM32-HAL库08-TIM的输出比较模式(输出PWM的另一种方式)

STM32-HAL库08-TIM的输出比较模式&#xff08;输出PWM的另一种方式&#xff09; 一、所用材料&#xff1a; STM32F103C6T6最小系统板 STM32CUBEMX&#xff08;HAL库软件&#xff09; MDK5 示波器或者逻辑分析仪 二、所学内容&#xff1a; 通过定时器TIM的输出比较模式得到预…...

【数据结构】深入浅出讲解计数排序【图文详解,搞懂计数排序这一篇就够了】

计数排序 前言一、计数排序算法核心思路映射 概念补充绝对映射相对映射 二、计数排序算法核心实现步骤三、码源详解四、效率分析&#xff08;1&#xff09;时间复杂度 — O&#xff08;Max&#xff08;N&#xff0c;range&#xff09;&#xff09;&#xff08;2&#xff09;空间…...

Canvas制作喷泉效果示例

Canvas能制作出很多动画效果&#xff0c;下面是一个制作喷泉效果的示例 效果图 源代码 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <meta name"viewport" content"widthdevice-width, initial-scale1 ,user-…...

什么是NPM(Node Package Manager)?它的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...

oracle如果不适用toad或者plsql工具如何获取索引建表语句

select dbms_lob.substr(dbms_metadata.get_ddl(INDEX,INDEX_NAME,DIXON))||; from dba_indexes where ownerDIXON这个语句可以获取dixon用户的所有索引创建语句&#xff0c;sql脚本形式呈现 点开一个语句查看 如果不使用dbms_lob.substr这个函数最后得到是一个clob selec…...

某大厂伺服驱动器量产方案

本文介一款大厂量产伺服驱动器方案&#xff01;带2500线省线式编码器&#xff0c;17位增量编码器&#xff0c;20位绝对值编码器&#xff01;标配CANopen、高精度运动控制&#xff0c;高速总线通讯&#xff0c;主芯片28335FPGA&#xff0c;已验证过&#xff0c;带can和485通讯&a…...

【计算机网络】网络层:数据平面

一.网络层概述 每台路由器的数据平面的主要功能时从其输入链路向其输出链路转发数据报&#xff0c;控制平面的主要功能是协调这些本地的每路由转发动作&#xff0c;使得数据报沿着源和目的地主机之间的路由器路径最终进行端到端传送。 网络层不运行运输层和应用层协议。 转发是…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型

在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重&#xff0c;适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解&#xff0c;并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...

在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7

在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤&#xff1a; 第一步&#xff1a; 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为&#xff1a; // 改为 v…...

[特殊字符] 手撸 Redis 互斥锁那些坑

&#x1f4d6; 手撸 Redis 互斥锁那些坑 最近搞业务遇到高并发下同一个 key 的互斥操作&#xff0c;想实现分布式环境下的互斥锁。于是私下顺手手撸了个基于 Redis 的简单互斥锁&#xff0c;也顺便跟 Redisson 的 RLock 机制对比了下&#xff0c;记录一波&#xff0c;别踩我踩过…...

算法—栈系列

一&#xff1a;删除字符串中的所有相邻重复项 class Solution { public:string removeDuplicates(string s) {stack<char> st;for(int i 0; i < s.size(); i){char target s[i];if(!st.empty() && target st.top())st.pop();elsest.push(s[i]);}string ret…...

DAY 45 超大力王爱学Python

来自超大力王的友情提示&#xff1a;在用tensordoard的时候一定一定要用绝对位置&#xff0c;例如&#xff1a;tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾&#xff1a; tensorboard的发展历史和原理tens…...

【版本控制】GitHub Desktop 入门教程与开源协作全流程解析

目录 0 引言1 GitHub Desktop 入门教程1.1 安装与基础配置1.2 核心功能使用指南仓库管理日常开发流程分支管理 2 GitHub 开源协作流程详解2.1 Fork & Pull Request 模型2.2 完整协作流程步骤步骤 1: Fork&#xff08;创建个人副本&#xff09;步骤 2: Clone&#xff08;克隆…...