Spring中FactoryBean的作用和实现原理

Spring中FactoryBean的作用和实现原理

BeanFactory与FactoryBean,相信很多刚翻看Spring源码的同学跟我一样很好奇这俩货怎么长得这么像,分别都是干啥用的。

  1. BeanFactory是Spring中Bean工厂的顶层接口,也是我们常说的SpringIOC容器,它定下了IOC容器的一些规范和常用方法并管理着Spring中所有的Bean;
  2. FactoryBean首先它是一个Bean,但又不仅仅是一个Bean。它是一个能生产或修饰对象生成的工厂Bean,类似于设计模式中的工厂模式和装饰器模式。它能在需要的时候生产一个对象,且不仅仅限于它自身,它能返回任何Bean的实例。

我们都知道在Spring中我们的Bean都会被Spring的IOC容器所管理,在AbstractApplicationContext中有一个很重要的方法:refresh(),项目启动或重启的时候refresh()会调用getBean()初始化所有的Bean,这个getBean()最终会指向AbstractBeanFactory中的getBean()方法。
在AbstractBeanFactory中,不管是根据名称还是根据类型,getBean()最终都会调用doGetBean(),在doGetBean()方法中一开始就获取了名称beanName和实例sharedInstance,这个方法太长,这里就贴前面两行。

        String beanName = this.transformedBeanName(name);
        Object sharedInstance = this.getSingleton(beanName);

transformedBeanName(name)是为了获取Bean真正的名称,它会去掉name前面的’&',而getSingleton(beanName)是从父类容器singletonObjects中取的这个Bean的实例。在Spring中还有很多这样的容器,比如DefaultListableBeanFactory中的beanDefinitionMap,它就是的IOC容器真正保存Bean的地方,它是一个HashMap。类似的还有FactoryBeanRegistrySupport中的factoryBeanObjectCache等。
回到doGetBean()方法中,拿到sharedInstance后,后面的一大堆操作做了单例、多例等判断,最终会走到this.getObjectForBeanInstance(),关键部分就在这个方法中,进入方法代码。

protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

		// Don't let calling code try to dereference the factory if the bean isn't a factory.
		if (BeanFactoryUtils.isFactoryDereference(name)) {
			if (beanInstance instanceof NullBean) {
				return beanInstance;
			}
			if (!(beanInstance instanceof FactoryBean)) {
				throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
			}
		}

		// Now we have the bean instance, which may be a normal bean or a FactoryBean.
		// If it's a FactoryBean, we use it to create a bean instance, unless the
		// caller actually wants a reference to the factory.
		if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
			return beanInstance;
		}

		Object object = null;
		if (mbd == null) {
			object = getCachedObjectForFactoryBean(beanName);
		}
		if (object == null) {
			// Return bean instance from factory.
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
			// Caches object obtained from FactoryBean if it is a singleton.
			if (mbd == null && containsBeanDefinition(beanName)) {
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}

在上面的代码中有两个判断分别是beanInstance instanceof FactoryBean和BeanFactoryUtils.isFactoryDereference(name),前面判断的是beanInstance是否属于FactoryBean或其子类的实例,后面这个方法判断name是否不为空且以&开头。

public static boolean isFactoryDereference(@Nullable String name) {
		return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
	}

如果beanInstance不属于FactoryBean或其子类的实例,或者name是以&开头就直接返回实例对象beanInstance,
否则继续往下走。各种if … ==null判断是为了提高性能,咱们只挑关键部分看object = getObjectFromFactoryBean(factory, beanName, !synthetic);

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
		if (factory.isSingleton() && containsSingleton(beanName)) {
			synchronized (getSingletonMutex()) {
				Object object = this.factoryBeanObjectCache.get(beanName);
				if (object == null) {
					object = doGetObjectFromFactoryBean(factory, beanName);
					// Only post-process and store if not put there already during getObject() call above
					// (e.g. because of circular reference processing triggered by custom getBean calls)
					Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
					if (alreadyThere != null) {
						object = alreadyThere;
					}
					else {
						if (shouldPostProcess) {
							if (isSingletonCurrentlyInCreation(beanName)) {
								// Temporarily return non-post-processed object, not storing it yet..
								return object;
							}
							beforeSingletonCreation(beanName);
							try {
								object = postProcessObjectFromFactoryBean(object, beanName);
							}
							catch (Throwable ex) {
								throw new BeanCreationException(beanName,
										"Post-processing of FactoryBean's singleton object failed", ex);
							}
							finally {
								afterSingletonCreation(beanName);
							}
						}
						if (containsSingleton(beanName)) {
							this.factoryBeanObjectCache.put(beanName, object);
						}
					}
				}
				return object;
			}
		}
		else {
			Object object = doGetObjectFromFactoryBean(factory, beanName);
			if (shouldPostProcess) {
				try {
					object = postProcessObjectFromFactoryBean(object, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
				}
			}
			return object;
		}
	}

看源码的时候如果我们一直追究所有的细节那会让我们会越陷越深,掉入细节的无底洞,稍不留神脑回路跟不上就会蒙圈。我们要学会找源码中的关键部分看,弄懂主要流程和本次看源码的目的的那部分就行。等我们对Spring整体有了一个很好的理解之后,再回头看之前不懂的代码就会豁然开朗。

在上面这个方法中不管是走上面的if分支还是到下边的else中,关键部分就是object = this.doGetObjectFromFactoryBean(factory, beanName)这段代码调用,继续点进去

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
			throws BeanCreationException {

		Object object;
		try {
			if (System.getSecurityManager() != null) {
				AccessControlContext acc = getAccessControlContext();
				try {
					object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				object = factory.getObject();
			}
		}
		catch (FactoryBeanNotInitializedException ex) {
			throw new BeanCurrentlyInCreationException(beanName, ex.toString());
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
		}

		// Do not accept a null value for a FactoryBean that's not fully
		// initialized yet: Many FactoryBeans just return null then.
		if (object == null) {
			if (isSingletonCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(
						beanName, "FactoryBean which is currently in creation returned null from getObject");
			}
			object = new NullBean();
		}
		return object;
	}

在这个方法中有一行关键代码:object = factory.getObject(); 这个factory就是我们传入的beanInstance实例。绕了这么一大圈,getBean方法返回的居然是我们实现FactoryBean接口定义的getObject方法。那么跟一开始对FactoryBean的描述印证了,FactoryBean是一个能生产或修饰对象生成的工厂Bean。一个Bean如果实现了FactoryBean接口,那么根据该Bean的名称获取到的实际上是getObject()返回的对象,而不是这个Bean自身实例,如果要获取这个Bean自身实例,那么需要在名称前面加上’&'符号。

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

package com.lvyuanj.core.service;

//接口
public interface FactoryBeanService {

    /**
     * 测试FactoryBean
     */
    void testFactoryBean();
}
package com.lvyuanj.core.service.Impl;

import com.lvyuanj.core.service.FactoryBeanService;

//实现类
public class FactoryBeanServiceImpl implements FactoryBeanService {

    @Override
    public void testFactoryBean() {
        System.out.println("我是FactoryBean的一个测试类。。。。");
    }
}
package com.lvyuanj.core.service.Impl;

import com.lvyuanj.core.service.FactoryBeanService;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

@Component
public class MyFactoryBean implements FactoryBean<FactoryBeanService> {

    @Override
    public FactoryBeanService getObject() throws Exception {
        //这个Bean是我们自己new的,这里我们就可以控制Bean的创建过程了
        return new FactoryBeanServiceImpl();
    }

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

    @Override
    public boolean isSingleton() {
        return true;
    }
}

@Test
	public void test2() throws Exception {
		ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
		String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
		for (String beanName : beanDefinitionNames) {
			System.out.println("beanName: " + beanName);
		}

		FactoryBeanService factoryBeanService = (FactoryBeanService) applicationContext.getBean("MyFactoryBean");
		System.out.println(factoryBeanService);
		MyFactoryBean factoryBeanService3 = (MyFactoryBean) applicationContext.getBean("&myFactoryBean");
		System.out.println(factoryBeanService3);
	}

在这里插入图片描述

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

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

相关文章

Android广播机制简介

文章目录 Android广播机制简介广播的基本概念广播的类型广播的使用场景Android广播的优缺点优点缺点 使用Android广播的一些最佳实践: Android广播机制简介 Android广播是一种轻量级的消息传递机制&#xff0c;用于应用程序之间或系统与应用程序之间进行通信。它类似于订阅-发…

ENG-2 AM,129423-53-6主要用于检测生物体系中的Na+浓度

引言&#xff1a;在化学研究的海洋中&#xff0c;优质的化学试剂是实验成功的关键。今天&#xff0c;我要为大家分享一款备受好评的化学试剂——ENG-2。这款试剂以其独特的性能和广泛的应用领域&#xff0c;赢得了众多科研人员的青睐。 中文名称&#xff1a;钠离子荧光探针ENG-…

[leetcode] 68. 文本左右对齐

文章目录 题目描述解题方法贪心java代码复杂度分析 题目描述 给定一个单词数组 words 和一个长度 maxWidth &#xff0c;重新排版单词&#xff0c;使其成为每行恰好有 maxWidth 个字符&#xff0c;且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单词&#xff…

运用远期交易防范外汇风险

随着全球化的深入&#xff0c;跨境贸易和投资愈加频繁&#xff0c;外汇风险成为各类企业和投资者必须面对的现实问题。汇率的波动可能导致交易和投资的成本大幅增加&#xff0c;甚至引发利润损失。在这种情况下&#xff0c;远期交易作为一种有效的外汇风险对冲工具&#xff0c;…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷1(私有云)

#需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包…

源代码加密

代码加密&#xff0c;特别是源代码加密&#xff0c;是一种安全措施&#xff0c;旨在保护软件的源代码不被未授权访问或泄露。源代码是软件应用程序的原始编程文本&#xff0c;它包含了程序的逻辑、算法和设计思想。由于源代码包含了软件的核心知识&#xff0c;因此它具有极高的…

数智算网,链启未来 | 算力网络子链诚邀各方加入

4月28日&#xff0c;在中国移动算力网络大会期间&#xff0c;由中国移动集团主办&#xff0c;中国移动研究院和云能力中心联合承办的“数智算网&#xff0c;链启未来”共链行动算力网络专场会议成功召开。中国移动研究院副院长段晓东&#xff0c;中国移动集团首席专家、云能力中…

MySQL·内置函数

目录 函数 日期函数 案例1&#xff1a;创建一张表&#xff0c;记录生日 案例2&#xff1a;创建一个留言表 案例3&#xff1a;请查询在2分钟内发布的帖子 字符串函数 案例1&#xff1a; 获取emp表的ename列的字符集 案例2&#xff1a;要求显示exam_result表中的信息&am…

Vinted店铺总被封号?如何有效养号?

Vinted是一家欧洲知名的二手时尚交易平台&#xff0c;致力于连接买家和卖家&#xff0c;让他们能够在平台上买卖二手时尚商品。用户可以在Vinted上销售和购买服装、鞋子、配饰等各种时尚物品&#xff0c;无论是品牌商品还是非品牌商品&#xff0c;都可以在平台上找到。Vinted的…

idea修改maven项目名称及子模块名称

一、修改目录名称 shift F6修改目录&#xff0c;选择“rename module and dictionary”。![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/43efd9c6af6e43ad9656455db94b37a2.png)二、修改子项目pom的 三、修改父项目pom的 四、刷新maven项目

JS笔试手撕题

数据劫持 Vue2的Object.defineProperty() Vue2的响应式是通过Object.defineProperty()拦截数据&#xff0c;将数据转换成getter/setter的形式&#xff0c;在访问数据的时候调用getter函数&#xff0c;在修改数据的时候调用setter函数。然后利用发布-订阅模式&#xff0c;在数…

Windows下启动Tomcat显示乱码解决办法

1、Windows下启动Tomcat显示乱码 2、解决办法 找到 D:\apache-tomcat-9.0.89\conf下的logging.properties&#xff0c;找到java.util.logging.ConsoleHandler.encoding的值改为GBK&#xff0c;就可以了 完美解决&#xff01;显示正常的中文了

网络安全之二层局域网封装及广域网封装详解

局域网封装&#xff1a;Ethernet2&#xff08;TCP/IP&#xff09;&#xff0c;IEEE802.3&#xff08;OSI&#xff09;&#xff08;前面文章中讲解了TCP、IP和OSI本文就不继续讲解&#xff1a;可以查看&#xff1a;网络安全之OSI七层模型详解-CSDN博客&#xff09; 广域网封装&…

『ZJUBCA Collaboration』WTF Academy 赞助支持

非常荣幸宣布&#xff0c;浙江大学区块链协会收到WTF Academy的赞助与支持&#xff0c;未来将共同开展更多深度合作。 WTF Academy是开发者的Web3开源大学&#xff0c;旨在通过开源教育让100,000名开发者进入到Web3。截止目前&#xff0c;WTF开源教程在GitHub收获超15,000 ⭐&a…

环形链表问题详解

引言 环形链表的题大家都应该做过&#xff0c;如果没有做过可以去某扣上做一下 ,下面有传送门 141. 环形链表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/linked-list-cycle/submissions/530160081/ 正文 如果在面试的情况下出现了环形链表的题大…

QLineEdit 最右侧添加按钮

如果采用QLineEdit + QPushButton的方式的话,无法将按钮放到QLineEdit的输入框内部,所以下面的方法可以将按钮放到QLineEdit内部的最右侧,效果: 代码如下: QLineEdit* editor = new QLineEdit(parent); QToolButton* btn = new QToolButton; btn->setText("...&q…

【噪声学习】噪声标签的鲁棒点云分割

Robust Point Cloud Segmentation with Noisy Annotations 事实上,与二维图像标注[1]、[2]相比,三维数据的干净标签更难获得。这主要是因为1)需要标注的点数通常非常庞大,例如在 ScanNetV2 [3] 中标注一个典型的室内场景时,需要标注百万量级的点数;2)标注过程本身更加复…

使用SmartEDA电路仿真软件,让这五件事变得很简单

SmartEDA电路仿真软件&#xff1a;轻松实现电子设计五大突破 在电子设计领域&#xff0c;SmartEDA电路仿真软件以其强大的功能和用户友好的界面&#xff0c;成为了设计师们的得力助手。这款软件不仅简化了电路设计流程&#xff0c;还提高了设计效率&#xff0c;让以下五件事情…

【驱动】I2C读写时序

1、I2C总线 I2C使用两条线在主控制器和从机之间通信,SCL(串行时钟线)和SDA(串行数据线),这两条线需接5~10欧上拉电阻,总线空闲空闲时,SCL和SDA处于高电平,I2C总线标准模式速度可以达到100K/S,快速模式可以达到400K/S。 2、状态 I2C总线有四种状态:空闲、启动、忙碌、…

品质为王:高效溶解性鱼油胶囊的软胶囊弹性硬度测试解析

品质为王&#xff1a;高效溶解性鱼油胶囊的软胶囊弹性硬度测试解析 在当今的健康产品市场中&#xff0c;高效溶解性鱼油胶囊以其独特的营养价值和吸收效率赢得了众多消费者的青睐。然而&#xff0c;要想在激烈的市场竞争中脱颖而出&#xff0c;产品的品质保证至关重要。其中&a…
最新文章