spring源码解析(二) 结合源码聊聊FactoryBean
一、什么是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中的加载过程
测试环境准备:
- 自定义一个FactoryBean实现类,返回对应的测试实例Person类,代码如图:

- 为了便于代码debug,使用xml的配置方式进行配置,如图:

- 启动代码如下:
通过以上环境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的区别
- BeanFactoryBeanFactory是spring定义的创建bean的一个顶层父接口,其定义了获取bean的方法,咱们常见的ApplicationContext和DefaultListableBeanFactory都间接实现了它;实现了BeanFactory意味着创建的bean必须要经过spring定义的复杂的各种生命周期方法;
- FactoryBeanFactoryBean同样可以创建bean,但是其管理的bean可以不经过spring定义的创建的bean的生命周期,也可以不通过反射直接获取;可以针对不同的需求定制化FactoryBean,相当于一个创建工厂的bean,而这个工厂bean又可以创建不同的对象实例
四、FactoryBean的实际运用场景
据我所知像Mybatis,dubbo以及springcloud这些开源框架中都有使用factoryBean来创建对象,但是由于本人并没有去看这些框架的源码也没有过多的了解所以这里暂且不表,所以在这里贴一个通过FactoryBean创建定时任务的例子;
项目中定时任务的使用还是比较频繁的,设置有时候会多到专门开一个服务器去跑这些定时任务,而定时任务无非关注几点,一是任务的间隔时间,二是任务的执行逻辑;
- 定义一个CommonTask来统一管理任务的间隔时间以及具体执行的service;
- 定义TaskFactoryBean实现FactoryBean接口,用来生成CommonTask的实例
- 定义一个TaskConfig类用来按需生成不同的CommonTask以上只是提供了粗略的实现思路,具体的等抽空补代码。。。
spring源码解析(二) 结合源码聊聊FactoryBean的更多相关文章
- RxJava2源码解析(二)
title: RxJava2源码解析(二) categories: 源码解析 tags: 源码解析 rxJava2 前言 本篇主要解析RxJava的线程切换的原理实现 subscribeOn 首先, ...
- Spring 循环引用(二)源码分析
Spring 循环引用(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Spring 循环引用相关文章: & ...
- Spring Boot REST(二)源码分析
Spring Boot REST(二)源码分析 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) SpringBoot RE ...
- Mybatis源码解析(二) —— 加载 Configuration
Mybatis源码解析(二) -- 加载 Configuration 正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...
- Sentinel源码解析二(Slot总览)
写在前面 本文继续来分析Sentinel的源码,上篇文章对Sentinel的调用过程做了深入分析,主要涉及到了两个概念:插槽链和Node节点.那么接下来我们就根据插槽链的调用关系来依次分析每个插槽(s ...
- [java源码解析]对HashMap源码的分析(二)
上文我们讲了HashMap那骚骚的逻辑结构,这一篇我们来吹吹它的实现思想,也就是算法层面.有兴趣看下或者回顾上一篇HashMap逻辑层面的,可以看下HashMap源码解析(一).使用了哈希表得“拉链法 ...
- Spring 源码解析之DispatcherServlet源码解析(五)
spring的整个请求流程都是围绕着DispatcherServlet进行的 类结构图 根据类的结构来说DispatcherServlet本身也是继承了HttpServlet的,所有的请求都是根据这一 ...
- iOS即时通讯之CocoaAsyncSocket源码解析二
原文 前言 本文承接上文:iOS即时通讯之CocoaAsyncSocket源码解析一 上文我们提到了GCDAsyncSocket的初始化,以及最终connect之前的准备工作,包括一些错误检查:本机地 ...
- jQuery 源码解析二:jQuery.fn.extend=jQuery.extend 方法探究
终于动笔开始 jQuery 源码解析第二篇,写文章还真是有难度,要把自已懂的表述清楚,要让别人听懂真的不是一见易事. 在 jQuery 源码解析一:jQuery 类库整体架构设计解析 一文,大致描述了 ...
随机推荐
- MySQL索引知识点&面试常见问题
来源:BiggerBoy 作者:北哥 原文链接:https://mp.weixin.qq.com/s/fucHvdRK5wRrDfBOo6IBGw 大家好我是北哥,今天整理了MySQL索引相关的知识点 ...
- Sum (欧拉定理)
题面 提示:无限输入 题解 一看这题的数据 ............................... 这也太大了,必须边输入边取模才行, 但是式子很复杂,所以必须推出一些结论. 因为Xk是有顺序 ...
- 【MySQL】从入门到精通5-一对多-外键
上期:[MySQL]从入门到掌握4-主键与Unique 第一章:创建角色表 啥是一对多啊? 一个账号可以有多个角色,但是一个角色只能属于一个账号. 举个例子,我们之前创建的是玩家的账号数据库. 但是一 ...
- Qt Q_OBJECT编译问题
编译问题 添加Q_OBJECT后需要qmake 多重继承 添加了Q_ENUM之类的宏,就需要Q_OBJECT 添加了Q_OBJECT,就需要类继承自QObject 如果有多重继承关系,QObject一 ...
- 为什么Index Only Scan却还需要访问表
在实际SQL优化工作中,我们经常会发现SQL 执行计划明明是 "Index Only Scan",但执行计划后面却有 "Heap Fetches: x" ,也就 ...
- KingbaseFlySync 无主键过滤器custompkey配置
无主键过滤器custompkey配置 1.执行如下命令:repkeyclean -dbtype kingbase8 -host 192.168.11.15 -port 54321 -user sy ...
- git stash总结
git stash 1. git stash save "message" 执行存储,并添加备注信息(直接git stash 也可以,但没有备注信息) 2. git stash ...
- 【Vue项目笔记】—— 父子组件之间传递参数和子组件执行父组件中的方法
父组件(MyBlog.vue) <template> <!-- Delete Modal --> <!-- 注意:这里的@deleteBlog中的deleteBlog要和 ...
- GreatSQL vs MySQL性能测试来了,速围观~
GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. GreatSQL是MySQL的国产分支版本,使用上与MySQL一致. 1.结论先行 无论ibp(innodb_buffer ...
- Elasticsearch: Ngrams, edge ngrams, and shingles
Ngrams和edge ngrams是在Elasticsearch中标记文本的两种更独特的方式. Ngrams是一种将一个标记分成一个单词的每个部分的多个子字符的方法. ngram和edge ngra ...