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服务管理控制台中显示,在注册表中 ...
随机推荐
- 时间加权平均价格算法(TWAP)和成交量平均算法(VWAP)在量化回测的应用
为什么要引入TWAP和 VWAP? 为了评估策略的资金容量,我们对M.trade模块里买入点和卖出点这两个参数进行了更丰富的扩展,支持了策略能够按更丰富的算法交易价格(WAP)进行撮合. 如果资金是1 ...
- CentOS连接网络设置
https://blog.csdn.net/tsundere_x/article/details/104263100 用这个 VMware提供了三种将虚拟网卡和物理网卡捆绑起来的方式,即桥接(Bri ...
- 高版本jdk的访问私有成员属性的正确姿势
在jdk17+已经不能直接通过 setAccessible 来访问私有属性了 Field name = access.getClass().getDeclaredField("name&qu ...
- libGDX游戏开发之Sprite、Texture和TextureRegion绘制旋转、反转(九)
libGDX游戏开发之Sprite.Texture和TextureRegion绘制反转(九) libGDX系列,游戏开发有unity3D巴拉巴拉的,为啥还用java开发?因为我是Java程序员emm- ...
- Java 8升级Java 11,升级必知要点!竟然有这些坑…
随着技术的不断进步,Java作为一种广泛使用的编程语言,其版本更新带来了许多新特性和性能提升.从Java 8升级到Java 11,是一个重要的转变,它不仅带来了新的编程范式,还引入了对现代软件开发的多 ...
- CodeForces 1141F2 贪心 离散化
CodeForces 1141F2 贪心 离散化 题意 给定一个序列,要求我们找出最多数量的不相交区间,每个区间和都相等. 思路 一开始没有头绪,不过看到 \(n \le 1500\) 后想到可以把所 ...
- 21、Scaffold属性 FloatingActionButton实现类似闲鱼App底 部导航凸起按钮
FloatingActionButton详解 FloatingActionButton简称FAB ,可以实现浮动按钮,也可以实现类似闲鱼app的底部凸起导航 实现类似闲鱼App底部导航凸起按钮 c ...
- 常用的 SQL
只知道字段名字查找表 SELECT table_name FROM information_schema.columns WHERE column_name = '字段名'; 查看不等于NULL的数据 ...
- Spring系列:基于注解的方式构建IOC
目录 一.搭建子模块spring6-ioc-annotation 二.添加配置类 三.使用注解定义 Bean 四.@Autowired注入 五.@Resource注入 六.全部代码 从 Java 5 ...
- Asp .Net Core系列:AutoMapper自动映射框架介绍、使用
1.介绍 AutoMapper是一个对象-对象映射器.对象-对象映射通过将一种类型的输入对象转换为另一种类型的输出对象来工作.使AutoMapper变得有趣的是,它提供了一些有趣的约定,以免去搞清楚如 ...