Spring - FactoryBean扩展接口

news/2024/12/27 3:49:45/

文章目录

  • Pre
  • org.springframework.beans.factory.FactoryBean
  • FactoryBean中的设计模式----工厂方法模式
  • FactoryBean VS BeanFactory
  • 源码解析
  • 扩展示例

在这里插入图片描述


Pre

Spring Boot - 扩展接口一览

在这里插入图片描述

org.springframework.beans.factory.FactoryBean


package org.springframework.beans.factory;import org.springframework.lang.Nullable;public interface FactoryBean<T> {String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";@NullableT getObject() throws Exception;@NullableClass<?> getObjectType();default boolean isSingleton() {return true;}}

在这里插入图片描述

一般情况下,Spring通过反射机制利用bean的class属性指定支线类去实例化bean,在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在bean中提供大量的配置信息。

配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。

FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。隐藏了实例化一些复杂bean的细节,给上层应用带来了便利。

从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式

在这里插入图片描述


FactoryBean中的设计模式----工厂方法模式

工厂方法模式是简单工厂模式的一种升级或者说是进一步抽象,它可以应用于更加复杂的场景,灵活性也更高。

在简单工厂中,由工厂类进行所有的逻辑判断、实例创建;

如果不想在工厂类中进行判断,可以为不同的产品提供不同的工厂,不同的工厂生产不同的产品,每一个工厂都只对应一个相应的对象,这就是工厂方法模式。

Spring 中的 FactoryBean 就是这种思想的体现,FactoryBean 可以理解为工厂 Bean


public interface FactoryBean<T> {T getObject()Class<?> getObjectType();boolean isSingleton();
}

我们定义一个类 ArtisanFactoryBean 来实现 FactoryBean 接口,主要是在 getObject 方法里 new 一个 Artisan对象。这样我们通过 getBean(id) 获得的是该工厂所产生的 Artisan 的实例,而不是 ArtisanFactoryBean本身的实例,像下面这样:

BeanFactory bf = new ClassPathXmlApplicationContext("artisan.xml");
Artisan  artisanBean = (Artisan) bf.getBean("artisanFactoryBean");

在这里插入图片描述


FactoryBean VS BeanFactory

  • BeanFactory,就是bean的工厂,主要是通过定位、加载、注册以及实例化来维护对象与对象之间的依赖关系,以此来管理bean

  • FactoryBean,bean的一种,顾名思义,它也可以用来生产bean,也实现了相应的工厂方法。

  • 一般来说,Bean是由BeanFactory生产,但FactoryBean的特殊之处就在于它也能生产bean。

  • 获取FactoryBean的方法是getBean("&"+beanName); 就是在beanName加个"&"前缀,若直接getBean(beanName),获取到的是FactoryBean通过getObject接口生成的Bean


源码解析

org.springframework.beans.factory.support.AbstractBeanFactory#getBeanorg.springframework.beans.factory.support.AbstractBeanFactory#doGetBean	org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean
 protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {/*** 通过name获取BeanName,这里不能使用name作为beanName:* 1. name可能是别名,通过方法转换为具体的实例名称* 2. name可能会以&开头,表明调用者想获取FactoryBean本身,而非FactoryBean创建bean*    FactoryBean 的实现类和其他的 bean 存储方式是一致的,即 <beanName, bean>,*    beanName 中是没有 & 这个字符的。所以我们需要将 name 的首字符 & 移除,这样才能从*    缓存里取到 FactoryBean 实例。**/final String beanName = transformedBeanName(name);Object bean;// 从缓存中获取beanObject sharedInstance = getSingleton(beanName);/** 如果 sharedInstance = null,则说明缓存里没有对应的实例,表明这个实例还没创建。*( BeanFactory 并不会在一开始就将所有的单例 bean 实例化好,而是在调用 getBean 获取bean 时再实例化,也就是懒加载)。* getBean 方法有很多重载,比如 getBean(String name, Object... args),我们在首次获取* 某个 bean 时,可以传入用于初始化 bean 的参数数组(args),BeanFactory 会根据这些参数* 去匹配合适的构造方法构造 bean 实例。当然,如果单例 bean 早已创建好,这里的 args 就没有* 用了,BeanFactory 不会多次实例化单例 bean。*/if (sharedInstance != null && args == null) {if (logger.isTraceEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.trace("Returning cached instance of singleton bean '" + beanName + "'");}}/** 如果 sharedInstance 是普通的单例 bean,下面的方法会直接返回。但如果* sharedInstance 是 FactoryBean 类型的,则需调用 getObject 工厂方法获取真正的* bean 实例。如果用户想获取 FactoryBean 本身,这里也不会做特别的处理,直接返回* 即可。毕竟 FactoryBean 的实现类本身也是一种 bean,只不过具有一点特殊的功能而已。*/bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}/** 如果上面的条件不满足,则表明 sharedInstance 可能为空,此时 beanName 对应的 bean* 实例可能还未创建。这里还存在另一种可能,如果当前容器有父容器,beanName 对应的 bean 实例* 可能是在父容器中被创建了,所以在创建实例前,需要先去父容器里检查一下。*/else {// BeanFactory 不缓存 Prototype 类型的 bean,无法处理该类型 bean 的循环依赖问题//判断是否存在循环依赖if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// 如果 sharedInstance = null,则到父容器中查找 bean 实例BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.String nameToLookup = originalBeanName(name);if (parentBeanFactory instanceof AbstractBeanFactory) {return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);}else if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else if (requiredType != null) {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}else {return (T) parentBeanFactory.getBean(nameToLookup);}}if (!typeCheckOnly) {markBeanAsCreated(beanName);}try {// 合并父 BeanDefinition 与子 BeanDefinitionfinal RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// 检查是否有 dependsOn 依赖,如果有则先初始化所依赖的 beanString[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dep : dependsOn) {/** 检测是否存在 depends-on 循环依赖,若存在则抛异常。比如 A 依赖 B,* B 又依赖 A,他们的配置如下:*   <bean id="beanA" class="BeanA" depends-on="beanB">*   <bean id="beanB" class="BeanB" depends-on="beanA">** beanA 要求 beanB 在其之前被创建,但 beanB 又要求 beanA 先于它* 创建。这个时候形成了循环,对于 depends-on 循环,Spring 会直接* 抛出异常*/if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}// 注册依赖记录registerDependentBean(dep, beanName);try {// 加载 depends-on 依赖getBean(dep);}catch (NoSuchBeanDefinitionException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"'" + beanName + "' depends on missing bean '" + dep + "'", ex);}}}// 创建 bean 实例if (mbd.isSingleton()) {/** 这里并没有直接调用 createBean 方法创建 bean 实例,而是通过* getSingleton(String, ObjectFactory) 方法获取 bean 实例。* getSingleton(String, ObjectFactory) 方法会在内部调用* ObjectFactory 的 getObject() 方法创建 bean,并会在创建完成后,* 将 bean 放入缓存中。*/sharedInstance = getSingleton(beanName, () -> {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}});// 如果 bean 是 FactoryBean 类型,则调用工厂方法获取真正的 bean 实例。否则直接返回 bean 实例bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}// 创建 prototype 类型的 bean 实例else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}// 创建其他类型的 bean 实例else {String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, () -> {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}// Check if required type matches the type of the actual bean instance.if (requiredType != null && !requiredType.isInstance(bean)) {try {T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);if (convertedBean == null) {throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}return convertedBean;}catch (TypeMismatchException ex) {if (logger.isTraceEnabled()) {logger.trace("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType) + "'", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;}

扩展示例

package com.artisan.bootspringextend.testextends;import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.Configuration;import javax.annotation.PostConstruct;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2022/12/5 23:45* @mark: show me the code , change the world*/@Slf4j
public class ExtendFactoryBean2 {public static void main(String[] args) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserServiceFactoryBean.class);definitionBuilder.addPropertyValue("username", "artisan");beanFactory.registerBeanDefinition("userService", definitionBuilder.getBeanDefinition());UserService userService = (UserService) beanFactory.getBean("userService");//artisanlog.info(userService.getUsername());UserServiceFactoryBean userServiceFactoryBean = (UserServiceFactoryBean) beanFactory.getBean("&userService");//artisanlog.info(userServiceFactoryBean.username);}public static class UserServiceFactoryBean implements FactoryBean<UserService> {@Setterprivate String username;@Overridepublic UserService getObject() {UserService userService = new UserService();userService.setUsername(username);return userService;}@Overridepublic Class<?> getObjectType() {return UserService.class;}}@Setter@Getterpublic static class UserService {private String username;}
}

定义一个UserServiceFactoryBean,用来生产UserService,将其注册到BeanFactory中,如果使用UserService对象,使用userServiceBean名称,
如果想要获取原来的UserServiceFactoryBean对象,需要使用&userService的Bean名称,&这个前缀是Spring规定的,可以查看BeanFactory#FACTORY_BEAN_PREFIX常量

00:05:24.692 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
00:05:24.762 [main] INFO com.artisan.bootspringextend.testextends.ExtendFactoryBean2 - artisan
00:05:24.762 [main] INFO com.artisan.bootspringextend.testextends.ExtendFactoryBean2 - artisanProcess finished with exit code 0

在这里插入图片描述


http://www.ppmy.cn/news/1891.html

相关文章

【git 介绍】AhuntSun

Git应用详解第一讲&#xff1a;Git分区&#xff0c;配置与日志 Git应用详解第二讲&#xff1a;Git删除、修改、撤销操作 Git应用详解第三讲&#xff1a;本地分支的重要操作 Git应用详解第四讲&#xff1a;版本回退的三种方式与stash Git应用详解第五讲&#xff1a;远程仓库…

19-29-k8s-基本命令-yaml-kubectl

19-k8s-基本命令-yaml-kubectl&#xff1a; Kubernetes 集群的命令行工具kubectl 1、kubectl 命令格式&#xff1a; kubectl [command] [type] [name] [flags] 参数&#xff1a; command&#xff1a;指定要对资源执行的操作&#xff0c;例如create、get、describe、delete t…

Thinkpad x13 锐龙安装 Archlinux 记录

硬件配置&#xff1a; 笔记本影响cpu显卡内存硬盘ThinkPad X13 锐龙版r7 4750U核显16g1TB 山寨固态&#xff08;大华&#xff09;镜像准备 https://archlinux.org/download/ http://mirrors.163.com/archlinux/iso/2022.12.01/ 每次安装都检查iso镜像是否是网站最新的&#x…

前端入门学习笔记四十七

<!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body><p>JavaScript 算数运算符</p><p id "a"></p><script>var x 9 9;document.getElemen…

模拟百度网盘

linux下基于多线程和tcp去实现了一个模拟网盘,实现cs之间的下载互传大文件,实现断点续传,秒传,利用md5进行校验,实时显示百分比 整体框架: 整个项目是在Linux环境下用C语言开发的,基于TCP协议,采用多线程的socket通信方式。 普通上传和秒传:客户端会先计算文件的MD…

浅析 em 和 rem

em 和 rem 都是相对长度单位 em 是相对于父级元素的font-size 大写来定义自身的大小 rem 是相对于根节点&#xff08;html{}, body{}, :root{}&#xff09;font-size来定义大小 Talk is cheap, show me the code !(空谈无用&#xff0c;上代码) em <html><head>…

【深度学习】常用算法生成对抗网络、自编码网络、多层感知机、反向传播等讲解(图文解释 超详细)

觉得有帮助请点赞关注收藏~~~ 一、生成对抗网络GAN Generative Adversarial Network 两个组件组成&#xff1a;一个生成器&#xff0c;用于生成虚拟数据&#xff0c;另一个是鉴别器&#xff0c;用于(GAN)生成式深度学习算法&#xff0c;可创建类似于训练数据的新数据实例。 G…

SpringBoot的创建的使用

哈喽呀&#xff0c;你好呀&#xff0c;欢迎呀&#xff0c;快来看一下这篇宝藏博客吧~~~ 目录 1、Spring Boot快速扫盲 2、Spring Boot 项目创建 3、运行项目 4 、输出hello world 5、注意事项--包路径错误 6、小结 1、Spring Boot快速扫盲 在创建SpringBoot项目前,我们得…