一、什么是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. 刷题记录:Codeforces Round #725 (Div. 3)

    Codeforces Round #725 (Div. 3) 20210704.网址:https://codeforces.com/contest/1538. 感觉这个比上一个要难. A 有一个n个数 ...

  2. 【JAVA】学习路径64-补充-编写一个会抛异常的方法

    有一些方法,在调用的时候有可能会出错,所以我们使用这些方法的时候会使用try catch. 比如InputStream里面的read()方法等等,那么这些方法是怎么实现抛异常的效果的呢? 能抛异常的方 ...

  3. 《吐血整理》进阶系列教程-拿捏Fiddler抓包教程(15)-Fiddler弱网测试,知否知否,应是必知必会

    1.简介 现在这个时代已经属于流量时代,用户对于App或者小程序之类的操作界面的数据和交互的要求也越来越高.对于测试人员弱网测试也是需要考验自己专业技术能力的一种技能.一个合格的测试人员,需要额外关注 ...

  4. Java开发学习(二十九)----Maven依赖传递、可选依赖、排除依赖解析

    现在的项目一般是拆分成一个个独立的模块,当在其他项目中想要使用独立出来的这些模块,只需要在其pom.xml使用<dependency>标签来进行jar包的引入即可. <depende ...

  5. 操作服务器的神奇工具Tmux

    Tmux 是什么? 会话与进程 命令行的典型使用方式是,打开一个终端窗口(terminal window,以下简称"窗口"),在里面输入命令.用户与计算机的这种临时的交互,称为一次 ...

  6. SonarQube支持Gitlab授权登录

    部署好SonarQube之后,由于我们内部使用的是自建的Gitlab仓库,即每个开发同学都有Gitlab账号,SonarQube我们就可以使用上Gitlab登录,这样就不需要再维护一套用户体系了. S ...

  7. 5G 与数字化转型的关系是怎样的?

    5G提供的是通信网络服务,数字化转型需要网络服务,但并不是必须使用5G网络,也就是说5G在数字化转型中并不是必虚的,但可以作为备选项,不过在某些行业比如农业.林业.牧业.港口.建筑等布设有线网络.无线 ...

  8. 欢迎来到ktq_cpp的cnblog

    欢迎来到ktq_cpp的cnblog 初学html,可能排版有一些问题,望指教 我的洛谷博客 我的AtCoder账号

  9. 关于JDK8中stream的用法小总结。

    import java.io.Serializable; import java.util.*; import java.util.stream.Collectors; public class Ma ...

  10. C语言实现扫雷游戏(完整版)

    头文件定义.函数声明 下面就是扫雷中使用到的所有函数,为了省事我把所有的代码都放在一个C文件中实现 宏定义中设置了游戏的界面布局,以及设置地雷的个数(这里默认的是10个地雷),界面是一个9*9的方格布 ...