文章目录

  • BeanFactory VS FactoryBean
  • FactoryBean VS 普通Bean
  • 演示
  • 源码
  • 使用场景


BeanFactory VS FactoryBean

首先明确一下,这两个东西是完全不同的两个东西 ,不要混淆。

BeanFactory 是Spring Framework的 顶级核心接口 , 没有这个接口,就没有Bean的产生。

FactoryBean也是一个接口,是一个特殊的Bean , 实现了FactoryBean 接口的Bean,原来的Bean将会被隐藏,而是由FactoryBean 的getObjecct方法返回最终的Bean 。 简单工厂模式 。

如果需要获取FactoryBean实例本身,需要 & 。

可以把FactoryBean理解为4S店,改装你原来的Bean。


FactoryBean VS 普通Bean

FactoryBean和普通Bean不同,其返回的对象不是指定类的一个实例,而是该FactoryBean的getObject方法所返回的对象。创建出来的对象是否属于单例由isSingleton中的返回决定。


演示


【Bean1】

package com.artisan.factoryBean;import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;@Component
public class Bean1 {@PostConstructpublic void init(){System.out.println("bean1 create ");}
}

【SpecialBeanFB 实现了 FactoryBean接口 】 用于装饰Bean

package com.artisan.factoryBean;import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;@Component
public class SpecialBeanFB implements FactoryBean {@PostConstructpublic void init(){System.out.println("SpecialBean as Factory Bean create ");}@Overridepublic Object getObject()  {return new Bean2();}@Overridepublic Class<?> getObjectType() {return Bean2.class;}@Overridepublic boolean isSingleton() {return true;}
}

如果SpecialBeanFB这个FactoryBean上的Component注解 增加了 name ,例如@Component("aaa") 其实是个getObject对象起的名字,而不是本身这个FactoryBean实例

当我们需要获取FactoryBean实例本身而不是它所产生的bean,要使用&符号

比如这里的 id为”specialBeanFB”的FactoryBean :

  • 调用getBean(“specialBeanFB”)将返回FactoryBean产生的bean
  • 调用getBean("&specialBeanFB")将返回FactoryBean它本身的实例

【配置类】

package com.artisan.factoryBean;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@ComponentScan("com.artisan.factoryBean")
public class FBConfig {}

【测试类】

package com.artisan.factoryBean;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class FactoryBeanTest {public static void main(String[] args) throws Exception {// 实例化Spring Bean 容器AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(FBConfig.class);// 从bean容器中读取普通的beanSystem.out.println(ac.getBean(Bean1.class));System.out.println(ac.getBean("bean1"));System.out.println("===========");// 从bean容器中读取FactoryBean接口修饰的BeanSystem.out.println(ac.getBean(SpecialBeanFB.class).getObject().getClass().getName());System.out.println(ac.getBean("specialBeanFB"));System.out.println("===========");// 通过 &  获取 FactoryBean本身的bean对象System.out.println(ac.getBean("&specialBeanFB"));System.out.println("===========");// 如果SpecialBeanFB这个FactoryBean上的Component注解 增加了 name ,例如@Component("aaa")// 其实是个getObject对象起的名字,而不是本身这个FactoryBean实例
//      System.out.println(ac.getBean("aaa"));
//      System.out.println(ac.getBean("&aaa"));}
}

【测试结果】


源码

在实例化Bean的方法中

AbstractApplicationContext # refresh() ---------- finishBeanFactoryInitialization(beanFactory) ----- DefaultListableBeanFactory#preInstantiateSingletons

在Spring容器启动阶段,会调用到refresh()方法,在refresh()中调用了finishBeanFactoryInitialization()方法,最终会调用到beanFactory.preInstantiateSingletons()方法

public void preInstantiateSingletons() throws BeansException {// 从容器中获取到所有的beanNameList<String> beanNames = new ArrayList<>(this.beanDefinitionNames);for (String beanName : beanNames) {// 合并Bean   ScannedGenericBeanDefinition AnnotatedGenericBeanDefinition 类型的Bean 等等 都要合并为 RootBeanDefinition 比较复杂,知道即可RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);// 创建Bean的条件校验if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {// 在此处会根据beanName判断bean是不是一个FactoryBean,实现了FactoryBean接口的bean,会返回true if (isFactoryBean(beanName)) {// 然后通过getBean()方法去获取或者创建单例对象// 注意:在此处为beanName拼接了一个前缀:FACTORY_BEAN_PREFIX  是一个常量字符串,即:&// 所以在此时容器启动阶段,对于specialBeanFB,应该是:getBean("&specialBeanFB")Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);// 下面这一段逻辑,是判断是否需要在容器启动阶段,就去实例化getObject()返回的对象,即是否调用FactoryBean的getObject()方法if (bean instanceof FactoryBean) {final FactoryBean<?> factory = (FactoryBean<?>) bean;boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)((SmartFactoryBean<?>) factory)::isEagerInit,getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}}}}
}

使用场景

比如 MyBatis3 提供 mybatis-spring项目中的 org.mybatis.spring.SqlSessionFactoryBean

SqlSessionFactoryBean 看名字的结尾FactoryBean

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {// ...省略其他代码public SqlSessionFactory getObject() throws Exception {if (this.sqlSessionFactory == null) {afterPropertiesSet();}return this.sqlSessionFactory;}
}

sqlSessionFactory是SqlSessionFactoryBean的一个属性,它的赋值是在通过回调afterPropertiesSet()方法进行的。 因为SqlSessionFactoryBean实现了InitializingBean接口,所以在Spring初始化Bean的时候,能回调afterPropertiesSet()方法 .

public void afterPropertiesSet() throws Exception {// buildSqlSessionFactory()方法会根据mybatis的配置进行初始化。this.sqlSessionFactory = buildSqlSessionFactory();
}

Spring5源码 - 08 BeanFactory和FactoryBean 源码解析 使用场景相关推荐

  1. Spring源码分析-从源码看BeanFactory和FactoryBean的区别

    导语   在使用Spring 中最为核心的操作就是Bean的创建以及使用.下面就来带着大家一起来分析一下关于Spring的Bean的加载相关的知识 文章目录 BeanFactory getBean方法 ...

  2. 修改meta标签 查看源码没效果怎么办_Spring 源码学习(三)-自定义标签

    又来填坑啦,上一篇讲完默认标签的解析,这篇笔记记录一下自定义标签的解析吧. 我们知道,Spring 源码的核心模块是 Spring-core 和 Spring-beans,在此基础上衍生出其他模块,例 ...

  3. crm开源系统 tp框架_thinkphp6学习教程与源码 tp6开源CMS系统源码研究

    thinkphp6最新正式版框架上市已经有一段时间了,从官方的介绍来看,tp6的框架和tp5有很大的区别,完全重新改写了底层架构代码和逻辑,所以不支持thinkphp5的无缝升级,也就是说如果你之前的 ...

  4. Android 源码分析之 EventBus 的源码解析

    1.EventBus 的使用 1.1 EventBus 简介 EventBus 是一款用于 Android 的事件发布-订阅总线,由 GreenRobot 开发,Gihub 地址是:EventBus. ...

  5. 扫码点餐小程序源码_扫码点餐小程序有什么用?怎么制作?

    现在小程序扫码点餐服务已经越来越普及,当用户需要点餐时,无需麻烦服务人员,只需扫描餐桌上或者海报上的小程序码,就能快速点餐下单.这样不仅节约了排队时间,也提高了商家自己的服务效率. 上线了小程序案例, ...

  6. android源码阅读笔记1-配置源码路径/阅读源码方法讨论

    开始之前 android studio中配置android源码路径 android studio中有源码的路径,你只需要打开SDK Manager下载源码然后重启android studio即可查看源 ...

  7. eclipse java jar源码,eclipse查看Jar包源码

    这几天想研究一下hibernate等流行开源框架的源码,于是了解了一下如何在eclipse中查看导入的jar包的源码. 我们在开发或学习过程中,有时总避免不了查看jar包的源码.当我们按Ctrl+点击 ...

  8. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  9. 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

最新文章

  1. R语言PCA主成分分析(Principle Component Analysis)实战1
  2. AdaBoost算法特性
  3. mysql最大并行用户设置_mysql 优化配置
  4. 计算机视觉与深度学习 | 目标提取(代码实现)
  5. CentOS 7 安装 JDK
  6. 实现位数超过32bit的整数的加减乘除运算_Java 运算符
  7. mysql 千万数据分页_MySQL处理千万级数据查询、分页
  8. B00003 C++标准库 std::bitset
  9. 模板 - 多项式快速插值
  10. Easyui清除tree的选中
  11. 计算机字体原理,字体科普文:认识最基础的字体结构 -电脑资料
  12. 【项目实战一】基于人工神经网络ANN的车牌识别
  13. Symantec Endpoint Protection(SEP) 离线病毒库下载与升级
  14. java中国象棋棋子走法,中国象棋的规则及各种棋子的走法介绍
  15. 虚幻开发工具包发布版本的版本信息
  16. 大屏需要JAVA什么技术_前端之大屏
  17. Mach3寻边和对刀代码
  18. LibreCAD_3编译遇到的问题
  19. 上海交通大学计算机系非全日制,关于上海交通大学非全日制研究生中最便宜的专业...
  20. wro4j wro.xml_WebJars和wro4j集成

热门文章

  1. 表格布局(tablelayout)
  2. php 立即释放session 去除其缓存,ThinkPHP关于session无法清除的一个小问题
  3. c++枚举类型(二) 命名空间
  4. C++基类和派生类的析构函数
  5. 打开了悬浮窗权限但是没有_给你的手机添加“樱花雨”特效,打开手机樱花就满屏飘落,漂亮!...
  6. Hadoop应用实战100讲(一)-Hadoop进行文件压缩
  7. MATLAB实战系列(五)-模拟退火(SA)算法求解旅行商 (TSP)问题MATLAB代码讲解
  8. Python_Statsmodels包_时间序列分析_ARIMA模型
  9. Matplotlib实例教程 | 统计DataFrame中文本长度分布(条形统计图)
  10. Python入门100题 | 第052题