浅谈Spring中的BeanFactory与FactoryBean

news2024/10/1 12:17:38

前言
理解FactoryBean是非常非常有必要的,因为在Spring中FactoryBean最为典型的一个应用就是用来创建AOP的代理对象,不仅如此,而且对理解Mybatis核心源码也非常有帮助!如果甘愿crud,做个快乐的码农,那我就哦豁豁豁豁豁豁豁豁豁豁豁豁豁豁…

文章目录

    • 1、BeanFactory
        • 1.1 BeanFactory 源码
        • 1.2、BeanFactory 使用场景
    • 2、FactoryBean
        • 2.1、为什么会有FactoryBean?
        • 2.2 、FactoryBean 源码
        • 2.3 、FactoryBean代码示例
        • 2.4 FactoryBean使用场景

BeanFactory和FactoryBean同样都是spring的接口,名字看起来很相似,但是我觉得要混淆还是很困难的!尽管Spring揭秘一书的作者都喜欢写上这一句。

请不要混淆BeanFactory 和 FactoryBean。

1、BeanFactory

BeanFactory,以Factory结尾,表示它是一个工厂(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是工厂的顶层接口,也是IOC容器的核心接口,因此BeanFactory中定义了管理Bean的通用方法,如 getBeancontainsBean 等,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,所以Spring容器给出了很多种实现,如 DefaultListableBeanFactoryXmlBeanFactoryApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。

1.1 BeanFactory 源码

public interface BeanFactory {
	//对FactoryBean的转义定义,因为如果使用bean的名字检索FactoryBean得到的对象是工厂生成的对象,
	//如果需要得到工厂本身,需要转义
	String FACTORY_BEAN_PREFIX = "&";

	//根据bean的名字,获取在IOC容器中得到bean实例
	Object getBean(String name) throws BeansException;

	//根据bean的名字和Class类型来得到bean实例,增加了类型安全验证机制。
	<T> T getBean(String name, @Nullable 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;

	//提供对bean的检索,看看是否在IOC容器有这个名字的bean
	boolean containsBean(String name);

	//根据bean名字得到bean实例,并同时判断这个bean是不是单例
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

	boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	//得到bean实例的Class类型
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	//得到bean的别名,如果根据别名检索,那么其原名也会被检索出来
	String[] getAliases(String name);
}

1.2、BeanFactory 使用场景

1、从Ioc容器中获取Bean(byName or byType)
2、检索Ioc容器中是否包含指定的Bean
3、判断Bean是否为单例

2、FactoryBean

首先FactoryBean是一个Bean,但又不仅仅是一个Bean,这样听起来矛盾,但为啥又这样说呢?其实在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个FactoryBean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似

2.1、为什么会有FactoryBean?

一般情况下,Spring通过反射机制利用的class属性指定实现类实例化Bean。至于为什么会有FactoryBean?原因有两个:

1、
在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。

2、
由于第三方库不能直接注册到spring容器,于是可以实现org.springframework.bean.factory.FactoryBean接口,然后给出自己对象的实例化代码即可。

2.2 、FactoryBean 源码

public interface FactoryBean<T> {
	//从工厂中获取bean【这个方法是FactoryBean的核心】
	@Nullable
	T getObject() throws Exception;
	
	//获取Bean工厂创建的对象的类型【注意这个方法主要作用是:该方法返回的类型是在ioc容器中getbean所匹配的类型】
	@Nullable
	Class<?> getObjectType();
	
	//Bean工厂创建的对象是否是单例模式
	default boolean isSingleton() {
		return true;
	}
}

方法介绍:
1、
getobject ()方法会返回该FactoryBean “生产” 的对象实例,我们需要实现该方法以给出自己的对象实例化逻辑;
2、
getobjectTYype ()方法仅返回getobject ()方法所返回的对象的类型,如果预先无法确定,则返回null; 特别注意这个方法主要作用是:该方法返回的类型是在ioc容器中getbean所匹配的类型,也就是说ioc中有很多类型的bean,要找到这个bean就是通过getobjectTYype ()方法的返回值类型!好吧,我举个例子

public class XXX implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        return new YYY;
    }       
    @Override
    public Class<?> getObjectType() {  //注意这个方法主要作用是:该方法返回的类型是在ioc容器中getbean所匹配的类型
        return AAA.class;
    }
}

那么要想在ioc中找到XXX这个类的bean(实际上是YYY) ,在getbean的时候写法如下

public class Demo1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = 
                new AnnotationConfigApplicationContext(Appconfig.class);
                
        annotationConfigApplicationContext.getBean( AAA.class ); // 【注意这里是AAA.class】

    }
}

3、
isSingleton ()方法返回结果用于表明,工厂方法(getobject ())所“生产”的对象是否要以singleton形式存在于容器中。如果以singleton形式存在,则返回true,否则返回false;

FactoryBean表现的是一个工厂的职责。 即一个Bean A如果实现了FactoryBean接口,那么A就变成了一个工厂,根据A的名称获取到的实际上是工厂调用getObject()返回的对象,而不是A本身,如果要获取工厂A自身的实例,那么需要在名称前面加上’&'符号。 通俗点表达就是

getObject(’ name ‘)返回工厂中的实例
getObject(’ &name ')返回工厂本身的实例

通常情况下,bean 无须自己实现工厂模式,Spring 容器担任了工厂的 角色;但少数情况下,容器中的 bean 本身就是工厂,作用是产生其他 bean 实例。由工厂 bean 产生的其他 bean 实例,不再由 Spring 容器产生,因此与普通 bean 的配置不同,不再需要提供 class 元素。

2.3 、FactoryBean代码示例

1、创建一个Appconfig类,扫描com.yichun下的所有子包

@Configuration
@ComponentScan("com.yichun")
public class Appconfig {
}

2、创建一个StudentBean类并实现FactoryBean,并重写其两个方法

@Component("studentBean")
public class StudentBean implements FactoryBean {
    @Override
    public Object getObject() throws Exception {
        return new TeacherBean();
    }
   
    @Override
    public Class<?> getObjectType() {  //注意这个方法主要作用是:该方法返回的类型是在ioc容器中getbean所匹配的类型
        return StudentBean.class;
    }
    //一个学生学习方法
    public void study(){
        System.out.println("学生学习。。。");
    }
}

3、再创建一个TeacherBean类

public class TeacherBean {
    public void teacher(){
        System.out.println("老师教书。。。。");
    }
}

4、测试StudentBean类型

public class Demo1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);
        StudentBean studentBean = (StudentBean)annotationConfigApplicationContext.getBean("studentBean");
        studentBean.study();
    }
}

在这里插入图片描述
加上“&”符号

public class Demo1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);
        //加上了“&”符号
        StudentBean studentBean = (StudentBean)annotationConfigApplicationContext.getBean("&studentBean");
        studentBean.study();
    }
}

在这里插入图片描述
运行成功

5、测试一下teacherBean类型

public class Demo1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);    
        TeacherBean teacherBean = (TeacherBean) annotationConfigApplicationContext.getBean("studentBean");       
        teacherBean.teacher();
    }
}

运行成功
在这里插入图片描述

2.4 FactoryBean使用场景

使用场景一:
FactoryBean在Spring中最为典型的一个应用就是用来创建AOP的代理对象。
我们知道AOP实际上是Spring在运行时创建了一个代理对象,也就是说这个对象,是我们在运行时创建的,而不是一开始就定义好的,这很符合工厂方法模式。更形象地说,AOP代理对象通过Java的反射机制,在运行时创建了一个代理对象,在代理对象的目标方法中根据业务要求织入了相应的方法。这个对象在Spring中就是——ProxyFactoryBean

所以,FactoryBean为我们实例化Bean提供了一个更为灵活的方式,我们可以通过FactoryBean创建出更为复杂的Bean实例。

当然在spring中,Spring容器内部许多地方了使用FactoryBean。下面是一些比较常见的FactoryBean实现:

JndiobjectFactoryBean
LocalSessionFactoryBean
SqlMapClientFactoryBean
ProxyFactoryBean
TransactionProxyFactoryBean

使用场景二:
Mybatis中的SqlSessionFactoryBean

<bean id="tradeSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="trade" />
        <property name="mapperLocations" value="classpath*:mapper/trade/*Mapper.xml" />
        <property name="configLocation" value="classpath:mybatis-config.xml" />
        <property name="typeAliasesPackage" value="com.bytebeats.mybatis3.domain.trade" />
    </bean>

package org.mybatis.spring;

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
        ......
}

另外提一下,阿里开源的分布式服务框架 Dubbo中的Consumer 也使用到了FactoryBean,

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
    ">
     
    <!-- 当前应用信息配置 -->
    <dubbo:application name="demo-consumer" />
   
   <!-- 暴露服务协议配置 -->
    <dubbo:protocol name="dubbo" port="20813" />    

    <!-- 暴露服务配置 -->
    <dubbo:reference id="demoService" interface="com.alibaba.dubbo.config.spring.api.DemoService"  />
     
</beans>

<dubbo:reference 对应的Bean是com.alibaba.dubbo.config.spring.ReferenceBean 类。

使用场景三:
Hibernate中的SessionFactoryBean。这里就不再概述。

最后本文难免会有不正之处,欢迎指正评判!欢迎指正评判!欢迎指正评判!!!

参考:
《Spring揭秘》王福强
https://zhuanlan.zhihu.com/p/87382038
https://www.cnblogs.com/aspirant/p/9082858.html

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

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

相关文章

Springcloud--docker快速入门

认识docker docker相关操作 1.初识Docker 1.1.什么是Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同组件之间部署时往往会产生一些冲突。在数百上千台服务中重复部署…

『Linux』第九讲:Linux多线程详解(三)_ 线程互斥 | 线程同步

「前言」文章是关于Linux多线程方面的知识&#xff0c;上一篇是 Linux多线程详解&#xff08;二&#xff09;&#xff0c;今天这篇是 Linux多线程详解&#xff08;三&#xff09;&#xff0c;内容大致是线程互斥与线程同步&#xff0c;讲解下面开始&#xff01; 「归属专栏」Li…

剑指 Offer 54. 二叉搜索树的第k大节点【37】

难度等级&#xff1a;容易 上一篇算法&#xff1a; 226. 翻转二叉树【58】 力扣此题地址&#xff1a; 剑指 Offer 54. 二叉搜索树的第k大节点 - 力扣&#xff08;Leetcode&#xff09; 1.题目&#xff1a;剑指 Offer 54. 二叉搜索树的第k大节点 给定一棵二叉搜索树&#xff0c…

springboot第15集:MyBatis分页

我们在测试SQL的时候&#xff0c;要是能够在控制台输出 SQL 的话&#xff0c;是不是就能够有更快的排错效率&#xff1f; 是的&#xff0c;输出 SQL 可以帮助我们更好地理解代码的执行流程和结果。在控制台输出 SQL 可以让我们看到实际执行的 SQL 语句&#xff0c;这样就能够更…

武忠祥老师每日一题||定积分基础训练(一)

若积分区间相同&#xff0c;最常用的思想&#xff1a; 若 f ( x ) > g ( x ) , 则 ∫ a b f ( x ) > ∫ a b g ( x ) 若f(x)>g(x),则\int_{a}^{b}f(x)>\int_{a}^{b}g(x) 若f(x)>g(x),则∫ab​f(x)>∫ab​g(x) 但 是 往 往 等 号 只 是 在 有 限 个 点 成…

Java实现添加文字水印、图片水印功能

Java实现添加水印功能 添加水印Java 2D API介绍绘制文字水印绘制图片水印循环添加文字水印 添加水印 为图片添加水印的主要作用是保护图片版权&#xff0c;防止图片被未经授权的人使用或传播。为图片添加水印是一种常用的图片处理技术。在Java 中可以使用JDK自带的 Graphics2D …

快速上手Django(九) -Django下载文件、Django上传文件、Django实现excel导入导出

文章目录 快速上手Django(九) -django 上传文件request.FILES&#xff0c;下载文件一、Django下载文件1. Django下载文件方案和思路2. HttpResponse、StreamingHttpResponse和FileResponse区别和选择StreamingHttpResponse和FileResponse对象的对比和选择使用HttpResponse使用S…

浏览器安全之XSS跨站脚本

基本概念 跨站脚本&#xff08;Cross-Site Scripting&#xff0c;XSS&#xff09;是一种经常出现在Web应用程序中的计算机安全漏洞&#xff0c;是由于Web应用程序对用户的输入过滤不足而产生的。 攻击者利用网站漏洞把恶意的脚本代码&#xff08;通常包括HTML代码和客户端Javas…

采用.Net Core技术框架开发的B/S版区域检验管理系统(云LIS)

实验室信息管理系统云LIS源码 SaaS模式运维管理系统 云LIS系统源码是一款全面的实验室信息管理系统源码&#xff0c;其主要功能包括样本管理、检测项目管理、质控管理、报告管理、数据分析、两癌筛查等多个方面。具有独立的配套SaaS模式运维管理系统&#xff0c;支持远程运维&…

AtCoder Beginner Contest 300——A-G题讲解

蒟蒻来讲题&#xff0c;还望大家喜。若哪有问题&#xff0c;大家尽可提&#xff01; Hello, 大家好哇&#xff01;本初中生蒟蒻讲解一下AtCoder Beginner Contest 300这场比赛的A-G题&#xff01; A - N-choice question 原题 Problem Statement Given integers A A A and…

数据埋点2

文章目录 1 数据埋点流程2 六个步骤实现数据埋点设计2.1 确认事件与变量2.2 明确事件的触发时机2.3 明确事件的上报机制2.4 设计数据表结构2.5 统一字段命名规范2.6 明确优先级 3 以电商购物成交转化为例实现数据埋点设计 作为数据分析师的你&#xff0c;是否和我一样经常会被业…

基于改进的离散PSO算法的FJSP的研究(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

排位赛结果“测谎”

甲乙丙丁说的关于排位赛结果只正确一半&#xff0c;根据甲乙丙丁说的信息求出正确的排位赛结果顺序。 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅仅是基础那…

chatGPT免费入口-ChatGPT国内中文版网站

chatgpt怎么使用 如果您有Chat GPT的API Key并想要使用Chat GPT进行自然语言处理或生成处理&#xff0c;您可以按照以下步骤操作&#xff1a; 安装必要的软件和工具 在使用Chat GPT之前&#xff0c;您需要先安装一些必要的软件和工具&#xff0c;例如Python解释器、HTTP客户…

【Android车载系列】第11章 系统服务-SystemServer自定义服务

1 编写自定义系统服务 1.1 AIDL接口定义 系统源码目录/frameworks/base/core/java/android/app/下新建AIDL接口IYvanManager.aidl package android.app;/** * 目录&#xff1a;/frameworks/base/core/java/android/app/IYvanManager.aidl */ interface IYvanManager{String …

YOLOV5入门讲解+常用数据集

大家好&#xff0c;我是csdn的博主&#xff1a;lqj_本人 这是我的个人博客主页&#xff1a; lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…

TIM-输出比较(PWM)——STM32

TIM-输出比较——STM32 Oc (Output Compare) 输出比较 输出比较可以通过比较CNT与CCR寄存器值的关系&#xff0c;来对输出电平进行置1、置0或翻转的操作&#xff0c;用于输出一定频率和占空比的PWM波形 每个高级定时器和通用定时器都拥有4个输出比较通道高级定时器的前3个通道…

2023年股票质押违约处置研究报告

第一章 行业概况 1.1 产品定义 股票质押违约处置是指在股票质押融资中&#xff0c;当质押人&#xff08;股票持有者&#xff09;无法按照约定履行还款义务时&#xff0c;质权人&#xff08;通常为金融机构&#xff09;对质押股票进行处置的一系列活动。这个行业涉及到多个领域…

DAY 55 mysql数据库管理

常用的数据类型&#xff1a; 类型说明int整型&#xff0c;用于定义整数类型的数据fload单精度浮点4字节32位&#xff0c;准确表示到小数点后六位double双精度浮点8字节64位char固定长度的字符类型&#xff0c;用于定义字符类型数据。varchar可变长度的字符类型text文本image图…

Python基础合集 练习23 (错误与异常语句处理3)

‘’’ raise语句 raise[ExceptionName[(reason)]] 其中ExceptionName[(reason)]是可选参数用来指定抛出异常名称和原因,如果省略该参数,就会原样输出当前的错误 ‘’’ 在下面程序中,使用raise语句抛出ValueError异常 def num_calu(): book int(input(输入图书数量: )) stu…