spring启动流程 (4) FactoryBean详解
FactoryBean接口
实现类对象将被用作创建Bean实例的工厂,即调用getObject()方法返回的对象才是真正要使用的Bean实例,而不是直接将FactoryBean对象作为暴露的Bean实例。
FactoryBeans可以支持singleton和prototype,并且可以根据需要懒加载或在启动时立即创建对象。
这个接口在编写扫描接口生成代理对象的场景下经常使用,比如Mybatis Mapper接口扫描、Dubbo接口扫描、Feign接口扫描等。
Spring容器只负责管理FactoryBean实例的生命周期,而不是FactoryBean创建的对象的生命周期。因此不会自动调用暴露的Bean对象的destroy方法。FactoryBean应该实现DisposableBean,并将关闭调用委托给底层对象。
public interface FactoryBean<T> {
/**
* Return an instance (possibly shared or independent) of the object
* managed by this factory.
*/
T getObject() throws Exception;
/**
* Return the type of object that this FactoryBean creates,
* or null if not known in advance.
*/
Class<?> getObjectType();
/**
* Is the object managed by this factory a singleton?
*/
default boolean isSingleton() {
return true;
}
}
我们可以使用正常的getBean(beanName)方式获取通过getObject()方法暴露的Bean实例,也可以使用getBean("&" + beanName)方式获取FactoryBean的实例。
下文将介绍其实现方式。
FactoryBean的实现方式
Spring创建单例Bean的逻辑在DefaultListableBeanFactory的preInstantiateSingletons()方法中:
public void preInstantiateSingletons() throws BeansException {
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
// 此处使用"& + beanName"作为beanName创建FactoryBean实例
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
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);
}
}
} else {
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
// ...
}
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(
String name, Class<T> requiredType, Object[] args, boolean typeCheckOnly)
throws BeansException {
// 此步骤将&符移除
String beanName = transformedBeanName(name);
Object bean;
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
} else if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
} else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
} else {
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
} catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
if (mbd.isSingleton()) {
// getSingleton方法中会先从单例池查找Bean
// 如果没有则会调用lambda中的createBean(beanName, mbd, args)创建Bean并将其注册到单例池
// 所以此时放入单例池的是FactoryBean实例
sharedInstance = getSingleton(beanName, () -> {
try {
// 此处的beanName参数是不带&符的字符串
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
// beanName参数是不带&符的字符串
// name是原始的带&符的字符串
// 该方法里面会判断name是否有&符,如果有直接返回FactoryBean实例
// 如果没有&符会调用getObject()方法获取真正要暴露的Bean实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
} finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean");
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
} finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException ex) {
throw new BeanCreationException(beanName, "", ex);
}
}
} catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
return (T) bean;
}
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
// 判断name有&符,直接返回FactoryBean实例
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;
}
if (!(beanInstance instanceof FactoryBean)) {
return beanInstance;
}
Object object = null;
if (mbd != null) {
mbd.isFactoryBean = true;
} else {
// 从factoryBeanObjectCache池获取
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()方法获取真正要暴露的Bean实例并将其放入factoryBeanObjectCache池
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
spring启动流程 (4) FactoryBean详解的更多相关文章
- Linux的启动流程以及GRUB详解
一.Linux引导和启动流程 概述,计算机电源接通后通过BISO之后,没有问题,就会去硬盘上找到MBR(Main Boot Record 主引导记录区)位于整个硬盘的0磁道0柱面1扇区, ...
- Tomcat5启动流程与配置详解
标签:配置 tomcat 休闲 职场 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://zhangjunhd.blog.51cto. ...
- 2017.3.31 spring mvc教程(二)核心流程及配置详解
学习的博客:http://elf8848.iteye.com/blog/875830/ 我项目中所用的版本:4.2.0.博客的时间比较早,11年的,学习的是Spring3 MVC.不知道版本上有没有变 ...
- Spring学习 6- Spring MVC (Spring MVC原理及配置详解)
百度的面试官问:Web容器,Servlet容器,SpringMVC容器的区别: 我还写了个文章,说明web容器与servlet容器的联系,参考:servlet单实例多线程模式 这个文章有web容器与s ...
- [PXE] Linux(centos6)中PXE 服务器搭建,PXE安装、启动及PXE理论详解
[PXE] Linux(centos6)中PXE 服务器搭建,PXE安装.启动及PXE理论详解 本篇blog主要讲述了[PXE] linux(centos)PXE无盘服务器搭建,安装,启动及pxe协议 ...
- Spring Boot Actuator监控使用详解
在企业级应用中,学习了如何进行SpringBoot应用的功能开发,以及如何写单元测试.集成测试等还是不够的.在实际的软件开发中还需要:应用程序的监控和管理.SpringBoot的Actuator模块实 ...
- DBA_Oracle Startup / Shutdown启动和关闭过程详解(概念)
2014-08-07 Created By BaoXinjian
- [置顶] 深入浅出Spring(三) AOP详解
上次的博文深入浅出Spring(二) IoC详解中,我为大家简单介绍了一下Spring框架核心内容中的IoC,接下来我们继续讲解另一个核心AOP(Aspect Oriented Programming ...
- Spring Boot的启动器Starter详解
Spring Boot的启动器Starter详解 作者:chszs,未经博主允许不得转载.经许可的转载需注明作者和博客主页:http://blog.csdn.net/chszs Spring Boot ...
- 服务启动项 Start类型详解
注册表的服务启动项 Start类型详解 HKLM\SYSTEM\CurrentControlSet\services\ 下的服务项.不论有没有在services.msc服务管理控制台中显示,在注册表中 ...
随机推荐
- ASR项目实战-交付过程中遇到的内核崩溃问题
当前参与交付的语音识别产品服务,算法模块基于经典的Kaldi,算法中的一部分运行在GPU之上. 算法团队采用的是声学模型+语言模型的1-pass方案.这个方案的特点在于,语言模型数据文件(HCLG文件 ...
- 热闹的人工智能VS酷寒的资本寒冬,2023年AI Agent项目盘点与融资分析
2023年国内AI Agent下项目大盘点,科技大厂与创业公司齐头并进 热闹人工智能VS酷寒资本寒冬,2023年AI Agent项目盘点与融资分析 资本寒冬下的AIGC,为什么这些AI Agent项目 ...
- CSS3学习笔记-字体属性
在CSS3中,可以使用字体属性来控制网页中文本的样式和排版.以下是常用的字体属性: font-family 该属性用于指定网页中的文本所使用的字体.我们可以通过使用通用的字体名称,或者直接使用字体名称 ...
- 使用Java 17中的record替代Lombok的部分功能
在DD长期更新的Java新特性专栏中,已经介绍过Java 16中开始支持的新特性:record的使用. 之前只是做了介绍,但没有结合之前的编码习惯或规范来聊聊未来的应用变化.最近正好因为互相revie ...
- 为什么匿名内部类可以实例化并实现抽象方法?lambda表达式是简化了匿名内部类的实现过程吗?
为什么匿名内部类可以实例化并实现抽象方法? 在Java中,接口是一种特殊的抽象类型,它只定义了一个或多个抽象方法.接口不能被实例化,但是我们可以使用匿名内部类来实现接口并创建一个具体的对象. 匿名内部 ...
- Spring源码学习笔记7——Spring bean的初始化
一丶前言 上篇中我们了解了Spring bean的实例化--存在方法覆盖的使用CGLIB动态代理生成子类,反之反射调用构造函数.实例化后bean中的字段都是默认值,接下来就是对bean的属性进行填充, ...
- LeetCode DFS、BFS篇(102、200、111、752)
102. 二叉树的层序遍历 给你一个二叉树,请你返回其按 层序遍历 得到的节点值. (即逐层地,从左到右访问所有节点). 示例: 二叉树:[3,9,20,null,null,15,7], 3 / 9 ...
- GaussDB(DWS)性能调优:Sort+Groupagg聚集引起的性能瓶颈案例
本文分享自华为云社区<GaussDB(DWS)性能调优:Sort+Groupagg聚集引起的性能瓶颈案例>,作者: O泡果奶~ . 本文针对SQL语句长时间执行不出来,且verbose执行 ...
- 技术解读丨目标检测之RepPoints系列算法
摘要:本文对anchor-free的目标检测RepPoints系列算法进行梳理,具体包含RepPoints, RepPoints V2, Dense RepPoints. 背景介绍 近两年来,anch ...
- 数仓专家面对面 | 为什么我选择GaussDB(DWS)
摘要:你知道数仓是如何应运而生的吗?你了解数仓未来的发展趋势吗?想知道国内数仓专家的看法吗? 导语 数据仓库的发展一直是备受关注的议题,随着近年来技术的不断演进,数仓也在更新迭代. 你知道数仓是如何应 ...