一、什么是FactoryBean

FactoryBean是由spring提供的用来让用户可以自定bean创建的接口;实现该接口可以让你的bean不用经过spring复杂的bean创建过程,但同时也能做到拿来即用,按需加载;该接口提供的方法如下:

/**
* 获取FactoryBean管理的对象的实例 一般由spring自动调用 无需手动调用
*/
T getObject() throws Exception; /**
* 获取FactoryBean管理的对象的类型
*/
Class<?> getObjectType(); /**
* 判断FactoryBean管理的对象是否是单例 默认为单例
* 如果返回为false,则spring不会使用factoryBeanObjectCache来缓存已加载的对象实例,
* 每次都会创建一个全新的对象
*/
default boolean isSingleton() {
return true;
}

二、FactoryBean在spring中的加载过程

     测试环境准备:

  1. 自定义一个FactoryBean实现类,返回对应的测试实例Person类,代码如图:
  2. 为了便于代码debug,使用xml的配置方式进行配置,如图:
  3. 启动代码如下:通过以上环境debug可以发现,当spring容器加载完后在BeanFactory的一级缓存singletonObjects中有myFactoryBean而并没有person类的实例,那ac.getBean("myFactoryBean")是怎么拿到Person的呢我们可以通过剖析getBean("myFactoryBean")这个方法来进行分析:当spring想要获取某个bean的时候,首先会从缓存中获取这个bean(参加org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)),当需要获取myFactoryBean时,由于xml中有对myFactoryBean的配置所以容器启动的时候myFactoryBean就已经被存到缓存中去了,所以从缓存中能直接获取,接下来会调用专门针对加载实现了FactoryBean接口的实现类的方法:getObjectForBeanInstance()==》getObjectFromFactoryBean()==》doGetObjectFromFactoryBean;其中会在doGetObjectFromFactoryBean中调用getObject方法返回具体的对象实例,getObjectFromFactoryBean方法中会根据isSingleton方法的具体实现判断要不要把对应实例加载到factoryBeanObjectCache缓存中;下面贴上这个三个核心方法的方法签名和源码:

org.springframework.beans.factory.support.AbstractBeanFactory#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.
* 判断是否是FactoryBean的子接口,是则设置isFactoryBean为true并返回
*/
if (BeanFactoryUtils.isFactoryDereference(name)) {
if (beanInstance instanceof NullBean) {
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
return beanInstance;
} // 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.
// 判断我们拿到的bean实例是不是factoryBean,如果不是直接返回
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
} Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
}
else {
// 从factoryBean的缓存factoryBeanObjectCache中尝试获取bean信息
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());
// 调用getObject获取factoryBean管理的bean实例
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}

            org.springframework.beans.factory.support.FactoryBeanRegistrySupport#getObjectFromFactoryBean:

点击查看代码
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;
}
}

          org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean:

点击查看代码
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, 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;
}

三、FactoryBean和BeanFactory的区别

  1.  BeanFactoryBeanFactory是spring定义的创建bean的一个顶层父接口,其定义了获取bean的方法,咱们常见的ApplicationContext和DefaultListableBeanFactory都间接实现了它;实现了BeanFactory意味着创建的bean必须要经过spring定义的复杂的各种生命周期方法;
  2. FactoryBeanFactoryBean同样可以创建bean,但是其管理的bean可以不经过spring定义的创建的bean的生命周期,也可以不通过反射直接获取;可以针对不同的需求定制化FactoryBean,相当于一个创建工厂的bean,而这个工厂bean又可以创建不同的对象实例

四、FactoryBean的实际运用场景

据我所知像Mybatis,dubbo以及springcloud这些开源框架中都有使用factoryBean来创建对象,但是由于本人并没有去看这些框架的源码也没有过多的了解所以这里暂且不表,所以在这里贴一个通过FactoryBean创建定时任务的例子;

项目中定时任务的使用还是比较频繁的,设置有时候会多到专门开一个服务器去跑这些定时任务,而定时任务无非关注几点,一是任务的间隔时间,二是任务的执行逻辑;

  1. 定义一个CommonTask来统一管理任务的间隔时间以及具体执行的service;
  2. 定义TaskFactoryBean实现FactoryBean接口,用来生成CommonTask的实例
  3. 定义一个TaskConfig类用来按需生成不同的CommonTask以上只是提供了粗略的实现思路,具体的等抽空补代码。。。

spring源码解析(二) 结合源码聊聊FactoryBean的更多相关文章

  1. RxJava2源码解析(二)

    title: RxJava2源码解析(二) categories: 源码解析 tags: 源码解析 rxJava2 前言 本篇主要解析RxJava的线程切换的原理实现 subscribeOn 首先, ...

  2. Spring 循环引用(二)源码分析

    Spring 循环引用(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring 循环引用相关文章: & ...

  3. Spring Boot REST(二)源码分析

    Spring Boot REST(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) SpringBoot RE ...

  4. Mybatis源码解析(二) —— 加载 Configuration

    Mybatis源码解析(二) -- 加载 Configuration    正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...

  5. Sentinel源码解析二(Slot总览)

    写在前面 本文继续来分析Sentinel的源码,上篇文章对Sentinel的调用过程做了深入分析,主要涉及到了两个概念:插槽链和Node节点.那么接下来我们就根据插槽链的调用关系来依次分析每个插槽(s ...

  6. [java源码解析]对HashMap源码的分析(二)

    上文我们讲了HashMap那骚骚的逻辑结构,这一篇我们来吹吹它的实现思想,也就是算法层面.有兴趣看下或者回顾上一篇HashMap逻辑层面的,可以看下HashMap源码解析(一).使用了哈希表得“拉链法 ...

  7. Spring 源码解析之DispatcherServlet源码解析(五)

    spring的整个请求流程都是围绕着DispatcherServlet进行的 类结构图 根据类的结构来说DispatcherServlet本身也是继承了HttpServlet的,所有的请求都是根据这一 ...

  8. iOS即时通讯之CocoaAsyncSocket源码解析二

    原文 前言 本文承接上文:iOS即时通讯之CocoaAsyncSocket源码解析一 上文我们提到了GCDAsyncSocket的初始化,以及最终connect之前的准备工作,包括一些错误检查:本机地 ...

  9. jQuery 源码解析二:jQuery.fn.extend=jQuery.extend 方法探究

    终于动笔开始 jQuery 源码解析第二篇,写文章还真是有难度,要把自已懂的表述清楚,要让别人听懂真的不是一见易事. 在 jQuery 源码解析一:jQuery 类库整体架构设计解析 一文,大致描述了 ...

随机推荐

  1. 【美国血统 American Heritage 题解】已知前序中序 求后序

    题目: 题目名称:美国血统 American Heritage 题目来源:美国血统 American Heritage ## 题目描述 农夫约翰非常认真地对待他的奶牛们的血统.然而他不是一个真正优秀的 ...

  2. 事物的隔离性和MVCC

    事物的隔离性 mysql的服务端是支持多个客户端同时与之连接的,每个客户端可能还并发了好几个连接,所以mysql是需要同时处理很多事情的,每一件独立的事情就叫做事务.我们知道事务有一个叫隔离性的特性, ...

  3. C# 使用if(DEBUG)调试 失效问题

    在调试winform程序的时候,经常会切换debug和release模式.有些时候在debug模式下不想用的东西就会使用 #if(!DEBUG) #endif 但是这次在新项目的时候,使用这个语句,失 ...

  4. Go 语言入门 3-动态数组(slice)的特性及实现原理

    go 语言中的动态数组(slice),是基于数组实现的,可以相比数组而言更加的灵活.其他语言的 slice 通常仅是一个 API, 但是 go 语言的 slice 不仅仅是一种操作, 也是一种数据结构 ...

  5. 用Python实现广度优先搜索

    图是一种善于处理关系型数据的数据结构,使用它可以很轻松地表示数据之间是如何关联的 图的实现形式有很多,最简单的方法之一就是用散列表 背景 图有两种经典的遍历方式:广度优先搜索和深度优先搜索.两者是相似 ...

  6. .NET 6当中的Web API版本控制

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进. 为了了解ASP.NET Core Web API的版本控制,我们必须了解API中的 ...

  7. Elasticsearch: analyzer

    在今天的文章中,我们来进一步了解analyzer. analyzer执行将输入字符流分解为token的过程,它一般发生在两个场合: 在indexing的时候,也即在建立索引的时候 在searching ...

  8. Logstash: 启动监控及集中管理-总结

    Logstash: 启动监控 配置文件:logstash.yml xpack.monitoring.enabled: true xpack.monitoring.elasticsearch.usern ...

  9. 【前端必会】前端开发利器VSCode

    介绍 工欲善其事必先利其器,开发工具方面选择一个自己用的顺手的,这里就用VSCode 安装参考 https://www.runoob.com/w3cnote/vscode-tutorial.html ...

  10. 密码学奇妙之旅、01 CFB密文反馈模式、AES标准、Golang代码

    CFB密文反馈模式 CFB密文反馈模式属于分组密码模式中的一种.加密与解密使用同一结构,加密步骤生成用于异或的密钥流. 其弥补了ECB电子密码本模式的不足(明文中的重复排列会反映在密文中,通过删除替换 ...