Spring5源码 - 08 BeanFactory和FactoryBean 源码解析 使用场景
文章目录
- 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 源码解析 使用场景相关推荐
- Spring源码分析-从源码看BeanFactory和FactoryBean的区别
导语 在使用Spring 中最为核心的操作就是Bean的创建以及使用.下面就来带着大家一起来分析一下关于Spring的Bean的加载相关的知识 文章目录 BeanFactory getBean方法 ...
- 修改meta标签 查看源码没效果怎么办_Spring 源码学习(三)-自定义标签
又来填坑啦,上一篇讲完默认标签的解析,这篇笔记记录一下自定义标签的解析吧. 我们知道,Spring 源码的核心模块是 Spring-core 和 Spring-beans,在此基础上衍生出其他模块,例 ...
- crm开源系统 tp框架_thinkphp6学习教程与源码 tp6开源CMS系统源码研究
thinkphp6最新正式版框架上市已经有一段时间了,从官方的介绍来看,tp6的框架和tp5有很大的区别,完全重新改写了底层架构代码和逻辑,所以不支持thinkphp5的无缝升级,也就是说如果你之前的 ...
- Android 源码分析之 EventBus 的源码解析
1.EventBus 的使用 1.1 EventBus 简介 EventBus 是一款用于 Android 的事件发布-订阅总线,由 GreenRobot 开发,Gihub 地址是:EventBus. ...
- 扫码点餐小程序源码_扫码点餐小程序有什么用?怎么制作?
现在小程序扫码点餐服务已经越来越普及,当用户需要点餐时,无需麻烦服务人员,只需扫描餐桌上或者海报上的小程序码,就能快速点餐下单.这样不仅节约了排队时间,也提高了商家自己的服务效率. 上线了小程序案例, ...
- android源码阅读笔记1-配置源码路径/阅读源码方法讨论
开始之前 android studio中配置android源码路径 android studio中有源码的路径,你只需要打开SDK Manager下载源码然后重启android studio即可查看源 ...
- eclipse java jar源码,eclipse查看Jar包源码
这几天想研究一下hibernate等流行开源框架的源码,于是了解了一下如何在eclipse中查看导入的jar包的源码. 我们在开发或学习过程中,有时总避免不了查看jar包的源码.当我们按Ctrl+点击 ...
- 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 二 )
Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...
- 【Android 事件分发】ItemTouchHelper 源码分析 ( OnItemTouchListener 事件监听器源码分析 )
Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...
最新文章
- R语言PCA主成分分析(Principle Component Analysis)实战1
- AdaBoost算法特性
- mysql最大并行用户设置_mysql 优化配置
- 计算机视觉与深度学习 | 目标提取(代码实现)
- CentOS 7 安装 JDK
- 实现位数超过32bit的整数的加减乘除运算_Java 运算符
- mysql 千万数据分页_MySQL处理千万级数据查询、分页
- B00003 C++标准库 std::bitset
- 模板 - 多项式快速插值
- Easyui清除tree的选中
- 计算机字体原理,字体科普文:认识最基础的字体结构 -电脑资料
- 【项目实战一】基于人工神经网络ANN的车牌识别
- Symantec Endpoint Protection(SEP) 离线病毒库下载与升级
- java中国象棋棋子走法,中国象棋的规则及各种棋子的走法介绍
- 虚幻开发工具包发布版本的版本信息
- 大屏需要JAVA什么技术_前端之大屏
- Mach3寻边和对刀代码
- LibreCAD_3编译遇到的问题
- 上海交通大学计算机系非全日制,关于上海交通大学非全日制研究生中最便宜的专业...
- wro4j wro.xml_WebJars和wro4j集成
热门文章
- 表格布局(tablelayout)
- php 立即释放session 去除其缓存,ThinkPHP关于session无法清除的一个小问题
- c++枚举类型(二) 命名空间
- C++基类和派生类的析构函数
- 打开了悬浮窗权限但是没有_给你的手机添加“樱花雨”特效,打开手机樱花就满屏飘落,漂亮!...
- Hadoop应用实战100讲(一)-Hadoop进行文件压缩
- MATLAB实战系列(五)-模拟退火(SA)算法求解旅行商 (TSP)问题MATLAB代码讲解
- Python_Statsmodels包_时间序列分析_ARIMA模型
- Matplotlib实例教程 | 统计DataFrame中文本长度分布(条形统计图)
- Python入门100题 | 第052题