FactoryBean是spring框架定义的bean工厂,即FactoryBean用于产生另外一个bean。FactoryBean可以用于解决初始化过程较为复杂,通过spring的依赖注入等无法解决的bean的初始化问题。

FactoryBean的定义

public interface FactoryBean<T> {String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";T getObject() throws Exception;Class<?> getObjectType();default boolean isSingleton() {return true;}
}
  • 是否单例指示

如果是单例,首次获取的bean会在FactoryBeanRegistrySupport中进行缓存。

如果是多例,那么每次获取工厂bean的Object都会生成一个新的bean。

  • 返回目标类型

该方法用于返回FactoryBean可以生成的目标bean的类型。这样可以避免为了获取bean的类型而进行提前init的问题。

  • 获取目标

该方法用于实现构造目标bean的逻辑。如果目标bean的构造依赖于其他的bean,那么FactoryBean也可以完成对这些被依赖的bean的注入管理。

FactoryBean的初始化在执行getObject方法之前,故当执行FactoryBean的getObject方法的时候,FactoryBean应该已经完成了实例化。

如果在FactoryBean当中注入一个目标bean,那么在FactoryBean的create过程当中会发生循环依赖问题。后面单独说明。

例子

public interface BlueFactoryBeanInterface extends FactoryBean<BlueBean> {void asyncLog(BlueBean blueBean);
}
@Builder
@Setter
@Getter
@ToString
public class BlueBean {private String name;private long index;
}
@Component
public class BlueFactoryBean implements BlueFactoryBeanInterface {private static long index = 0;@Autowiredprivate BlueFactoryBeanInterface blueFactoryBean;@Autowired@Lazyprivate BlueFactoryBeanInterface blueFactoryBeanProxy;//@Autowired//private BlueBean blue;@Autowired@Lazyprivate BlueBean blueProxy;@Asyncpublic void asyncLog(BlueBean blueBean) {System.out.println("asynchrounos log: BlueFactoryBean say hello " + blueBean);}@Overridepublic BlueBean getObject() throws Exception {//build the BlueBeanBlueBean bean = BlueBean.builder().name("blue").index(index++).build();System.out.println("getObject " + bean);//print log info in asynchronous way when new object generated.blueFactoryBeanProxy.asyncLog(bean);return bean;}@Overridepublic Class<?> getObjectType() {return BlueBean.class;}@Overridepublic boolean isSingleton() {return false;}

我们定义一个FactoryBean接口的实现BlueFactoryBean。这个类能够生产多例BlueBean,每次调用getBean(''blueFactoryBean'')都会返回一个新的BlueBean。同时,在每次产生一个新的bean的时候,都会异步打印一条日志。这个类的组织关系如下图所示。

为什么保存FactoryBean而不是目标?

在容器当中保存的是FactoryBean本身,而不是目标。

blueFactoryBean=org.blue.bluelearning.springproject.factory_bean.BlueFactoryBean@3a8d467e

FactoryBean有可能根据isSingleton的配置来判断每次是否返回一个新的Object。假设直接保存Object而不是FactoryBean,则无法通过FactoryBean来实现这个功能。单独保存Object也是没有必要的,因为在spring getBean的实现逻辑当中,首次获取到的target object会被缓存在FactoryBeanRegistrySupport。

org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBean<?>, String, boolean)

因此,如果为多例。则每次的注入点解析,getBean(''blueFactoryBean'')实际上都可以返回一个新的bean。

总之,通过保存FactoryBean,再通过FactoryBean获取目标bean,这样可以实现更加灵活的bean的生成。

FactoryBean的获取

SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableAsync
public class App {public static void main(String[] args) throws Exception {//基于cglibConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(App.class, args);BlueBean bean1 = configurableApplicationContext.getBean(BlueBean.class);FactoryBean<BlueBean> bean2 =(FactoryBean<BlueBean>)configurableApplicationContext.getBean(BlueFactoryBeanInterface.class);FactoryBean<BlueBean> bean3 =(FactoryBean<BlueBean>)configurableApplicationContext.getBean(BeanFactory.FACTORY_BEAN_PREFIX+"blueFactoryBean");BlueBean bean4 =(BlueBean)configurableApplicationContext.getBean("blueFactoryBean");//FactoryBean bean = configurableApplicationContext.getBean(FactoryBean.class);}
}

(一)按照名称获取

在BeanFactory中保存的bean的类型实际上是一个FactoryBean,并且这个FactoryBean的名字就是@Component中指定的bean名称或者bean class name的首字母缩写,比如本例中的blueFactoryBean。

如果执行getBean("blueFactoryBean")会得到Object

如果执行getBean("&blueFactoryBean")会得到FactoryBean本身。

(二)按照类型获取

和按照名称获取不同,按照类型获取到的bean的类型就是指定的类型。

如果执行getBean(BlueFactoryBeanInterface.class)会得到FactoryBean本身。

如果执行getBean(BlueBean.class)会得到FactoryBean本身。当然,spring会首先获取到BlueFactoryBean,再通过BlueFactoryBean得到BlueBean。

这里定义了BlueFactoryBeanInterface,是为了便于根据这个类型得到惟一的一个bean,如果根据FactoryBean去查找,会得到多个候选。

和循环依赖

(一)getObjectForBeanInstance

AbstractBeanFactory.doGetBean

从缓存当中获取到目标

-getSingleton(beanName)

-getObjectForBeanInstance(sharedInstance, beanName, name)

OR(新建bean)

-getSingleton(beanName, ObjectFactory)

--createBean()

--addSingleton

-getObjectForBeanInstance

可以看到无论是从缓存当中获取到目标,还是新建一个bean(在新建bean的过程当中可能发生循环依赖问题),都是在getSingleton结束之后,才会执行getObjectForBeanInstance。

从上面可以看到FactoryBean的实例化,和普通bean的实例化是完全相同。自然也可能出现循环依赖问题。在FactoryBean中注入的实例,可以用于完成目标对象的实例化。

但是getObjectForBeanInstance一般不会导致循环依赖的产生。

(二)getObject方法

FactoryBean的getObject方法似乎和循环依赖问题关联不大。不过getObject方法的不合理的定义会产生意想不到的问题。

  • 注入点的解析

如果要要注入一个BlueBean,那么getBean方法就会执行getObject方法来在BlueFactoryBean的基础上生成一个BlueBean。要在FactoryBean中注入的是一个BlueBean。

Spring会认为这是循环依赖,因为FactoryBean的非带前缀名称就是BlueBean的名称。

  • getObject的嵌套

如果在getObject的逻辑当中触发getObject会造成overstack问题。

public class BlueFactoryBean implements BlueFactoryBeanInterface {private static long index = 0;@Autowiredprivate BlueBean blue;@Asyncpublic void asyncLog(BlueBean blueBean) {System.out.println("asynchrounos log: BlueFactoryBean say hello" + blueBean);}@Overridepublic BlueBean getObject() throws Exception {//build the BlueBeanBlueBean bean = BlueBean.builder().name("blue").index(index++).build();System.out.println("getObject " + bean);/*** below code will case an overstack error, * getObject()->resolve blue injection->getObject()-> ......*/System.out.println("The injected blue's index is: " + blue.getIndex());//print log info in asynchronous way when new object generated.blueFactoryBeanProxy.asyncLog(bean);return bean;}
}

(三)在BlueFactoryBean注入一个BlueBean

如果在BlueFactoryBean当中注入一个BlueBean,则会产生一个循环依赖的报错问题。代码如下。当然循环依赖问题的万能注解@Lazy可以解决这个问题。不过需要具体分析一下报错的原因。

@Component
public class BlueFactoryBean implements BlueFactoryBeanInterface {......@Autowiredprivate BlueBean blue;@Asyncpublic void asyncLog(BlueBean blueBean) {System.out.println("asynchrounos log: BlueFactoryBean say hello " + blueBean);}......
}

在spring的启动阶段会遍历容器所有的单例bean,对他们进行提前的预初始化。BlueFactoryBean就是一个单例。这个单例的名字是blueFactoryBean,因此执行逻辑如下:

getBean("blueFactoryBean")

- expose early reference

- poputeBean

        -- resolve blue

        --- 找到 BlueFactoryBean 可以生产 BlueBean

        --- 执行getBean("blueFactoryBean")

        --- 注入一个BlueBean。

        --- 注册依赖 blueFactoryBean(注入的bean的名字就是BlueBean) -> blueFactoryBean(当前bean的名字)

-initializeBean

--对BlueFactoryBean增强为 Proxy(BlueFactoryBean)

-check

-- 发现BlueFactoryBean 被增强,并且 他已经提前注册到了另外一个bean里面(就是自己),所以报错。

我个人认为,这里的检查报错的原因并非BlueFactoryBean的提前注册,而是因为 BlueFactoryBean的getObject方法的提前执行。BlueFactoryBean没有完全完成初始化之前,就进行了目标的生产,并且后续BlueFactoryBean又进行了增强,所以肯定存在隐患。

(四)再提名字

由在BlueFactoryBean中注入一个BlueBean的分析过程可以知道,在成功注入一个BlueBean之后,会在BeanFactory当中注册依赖。

blueFactoryBean(注入的bean的名字就是BlueBean) -> blueFactoryBean(当前bean的名字)

我个人认为,这个依赖的注册是spring能够检测到循环依赖问题,并且报错的关键。但是问题是,为什么注册了一个BlueBean,但是这里使用的名字却是blueFactoryBean?

虽然,容器里面注册的是 FactoryBean,甚至名字也是FactoryBean的名字,但是实际上是用于获取一个它的产品。 所以,FactoryBean的名字也理所当然的是它所生产的产品的名字。而他自己的名字需要增 加一个&前缀。BlueBean在容器当中并没有名字,因为它没有在容器当中直接进行注册,它的名字就是工厂bean的名字,而工厂bean的名字是需要添加&前缀。

(五)在BlueFactoryBean注入一个BlueFactoryBean

因为名字FactoryBean自身对应的bean名称的特殊性,在BlueFactoryBean中注入自身并不会出现循环依赖的报错。

@Component
public class BlueFactoryBean implements BlueFactoryBeanInterface {......@Autowiredprivate BlueFactoryBean blueFactoryBean;@Asyncpublic void asyncLog(BlueBean blueBean) {System.out.println("asynchrounos log: BlueFactoryBean say hello " + blueBean);}......
}

虽然为发生报错,但是由于是在create BlueFactoryBean过程当中提前获取的为增强raw bean。所以注入的bean并非最终版本。也是有问题的。下面分析一下为什么没有发生报错问题。

getBean("blueFactoryBean")

- expose early reference

- poputeBean

        -- resolve blueFactoryBean

        --- 执行getBean(BlueFactoryBean.class)

        --- 注入一个BlueFactoryBean。

        --- 注册依赖 &blueFactoryBean(注入的bean的名字就是BlueBean) -> blueFactoryBean(当前bean的名字)

-initializeBean

--对BlueFactoryBean增强为 Proxy(BlueFactoryBean)

-check

-- 发现BlueFactoryBean 被增强,但是没有发现他已经提前注册到了另外一个bean里面。

原因是这里注册依赖是

&blueFactoryBean(注入的bean的名字就是BlueBean) -> blueFactoryBean(当前bean的名字)

        getBean在create过程当中总是认为自己的名字是blueFactoryBean,因为具体判断是返回自身还是产品是在create BlueFactoryBean之后。

(六)@Lazy

尽管直接注入BlueFactoryBean或者BlueBean都是有问题的,但是@Lazy这个万能注解可以很好的解决这个问题。

和三级缓存

三级缓存用于应对循环依赖问题,而FactoryBean的create过程和其他bean的create过程无异,必然也会和三级缓存相关。

DefaultSingletonBeanRegistry.getSingleton(String beanName,ObjectFactory<?> singletonFactory)

-1. objectFactory.getObject():实际为createBean()方法。

-2. DefaultSingletonBeanRegistry.addSingleton(String, Object)

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.addSingleton(String, Object)
protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}
}

可以看到getSingleton执行完之后,单例已经放到了singletonObjects当中,三级缓存的作用域已经结束。所以,getObjectForBeanInstance获取目标target object的过程和三级缓存没有直接关系。但是FactoryBean的create过程还是和三级缓存直接相关的。

FactoryBean of Spring相关推荐

  1. Spring注解驱动开发第11讲——面试官让我说说:如何使用FactoryBean向Spring容器中注册bean?

    写在前面 经过前面的学习,我们知道可以通过多种方式向Spring容器中注册bean.可以使用@Configuration注解结合@Bean注解向Spring容器中注册bean:可以按照条件向Sprin ...

  2. Spring基础16——使用FactoryBean来创建

    1.配置bean的方式 配置bean有三种方式:通过全类名(class反射).通过工厂方法(静态工厂&实例工厂).通过FactoryBean.前面我们已经一起学习过全类名方式和工厂方法方式,下 ...

  3. Spring bean 之 FactoryBean

    原文地址:http://www.carlzone.cn/spring... Spring中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean,即FactoryBean.Spring Fac ...

  4. spring FactoryBean的知识应用和Beanfactory的区别

    BeanFactory:是Spring的一个工厂它能够得到Spring工厂给创建的Bean,能通过getBean()方法获得. FactoryBean:是一个特殊的Bean,Spring管理它的时候会 ...

  5. Spring @Configuration和FactoryBean

    考虑使用FactoryBean通过Spring配置文件定义缓存: <cache:annotation-driven /><context:component-scan base-pa ...

  6. java中factory_Java后台面试--Spring中FactoryBean与BeanFactory的使用及区别

    以前刚转Java的时候去面试被问到过Spring中FactoryBean与BeanFactory的使用及区别,由于之前没有重视这两个的区别,只是在配置文件里面加bean结点并通过注解的形式调用,所以被 ...

  7. Spring揭秘 读书笔记 三 bean的scope与FactoryBean

    本书可作为王富强所著<<Spring揭秘>>一书的读书笔记  第四章 BeanFactory的xml之旅 bean的scope scope有时被翻译为"作用域&quo ...

  8. 【Spring】FactoryBean和普通Bean的区别

    [提问] Spring中所说的FactoryBean和普通Bean的区别有什么区别? [解答] 具体区别: 维度不同 Bean只是一个概念,统称所有被SpringIOC工厂(容器)管理的对象,硬要说也 ...

  9. 浅谈Spring中的BeanFactory与FactoryBean

    前言 理解FactoryBean是非常非常有必要的,因为在Spring中FactoryBean最为典型的一个应用就是用来创建AOP的代理对象,不仅如此,而且对理解Mybatis核心源码也非常有帮助!如 ...

最新文章

  1. 1.Maven之(一)Maven是什么
  2. Servicehot和你说说运维自动化的那些事儿
  3. AnimatorController即动画控制器创建的BUG
  4. 【深度学习】深入理解LSTM
  5. HandlerExceptionResolvers
  6. 2.3微秒的特征点匹配
  7. 能源36号文解读_IDC报告预测:今年中国新能源汽车销量将达116万辆,未来五年复合增长率36%_详细解读_最新资讯_热点事件...
  8. webgis从基础到开发实践_ArcGIS API For Javascript 开发笔记(四)
  9. 【转载】IPPROTO_RAW IPPROTO_IP
  10. 微信公众平台开发教程(二) 基本原理及消息接口
  11. L1-035 情人节 (15 分)—团体程序设计天梯赛
  12. boot客户管理系统源码_开源 SpringBoot+vueJs 前后端管理系统模版
  13. dotween上下摆动_DOTWeen插件使用技巧
  14. Canvas API(画布)简介
  15. 不小心把桌面上的计算机图标删了怎么办,如何删除桌面图标,不小心把桌面上的我的电脑图标删除了怎么办...
  16. whisper客服源码_开源在线客服系统whisper
  17. java对文件分片处理
  18. Scalar数据类型
  19. 李宏毅机器学习作业4——Recurrent Neural Network
  20. Android BLE 蓝牙开发指南(三)外围设备端开发详解

热门文章

  1. 北京超级计算机中心,超级计算机“元”上线 北京超云中心正式服役
  2. vue项目接入视频监控系列-------播放器的选择
  3. python自动识别简单图片中的文字
  4. 【触摸屏功能测试】昆仑通态MCGS——物联网功能测试
  5. MySql-基础查询与排序
  6. 为什么很多人工作都不开心?(转)
  7. 当编程语言都变成女孩子,你会不会喜欢她们!
  8. 我与我的专业计算机作文500字,电脑让我欢喜让我忧作文500字
  9. 关于 Docker 容器中东八区时间设置问题的总结
  10. 荣耀android手机怎么截图,华为荣耀畅玩5C怎么截图/截屏方法教程