Spring FactoryBean 缓存
相关文章
- Spring 整体架构
- 编译Spring5.2.0源码
- Spring-资源加载
- Spring 容器的初始化
- Spring-AliasRegistry
- Spring 获取单例流程(一)
- Spring 获取单例流程(二)
- Spring 获取单例流程(三)
- Spring 循环依赖
- ..................................
读完这篇文章你将会收获到
DisposableBean
的destroy
执行Spring
如何缓存FactoryBean
产生的单例bean
- 如何解决
FctoryBean
的getObject
的循环依赖
不多BB , 上图
SingletonBeanRegistry
今天我们来扯一下 SingletonBeanRegistry
, 我们向 Spring
容器注册 bean
的时候就是用到这个接口的方法
public interface SingletonBeanRegistry {
void registerSingleton(String beanName, Object singletonObject);
@Nullable
Object getSingleton(String beanName);
boolean containsSingleton(String beanName);
String[] getSingletonNames();
int getSingletonCount();
// 并发控制 现在的实现都是 使用 第一级缓存的 singletonObjects 对象
Object getSingletonMutex();
}
DefaultSingletonBeanRegistry
我们先看看这个接口的默认实现类 DefaultSingletonBeanRegistry
我们前几篇文章说的三级缓存就是在这里定义的
/**
* 第一级缓存
*/
Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* 第三级缓存
*/
Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**
* 第二级缓存
**/
Map<String, Object> earlySingletonObjects = new HashMap<>(16);
/**
* 只要是加入到三级缓存中到 beanName 都会被注册到这个 set 无论是第三级缓存或者是第一级缓存
*/
Set<String> registeredSingletons = new LinkedHashSet<>(256);
/**
* Names of beans that are currently in creation.
* 正在处于一个创建状态的 bean、存放的是 beanName
*/
Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
* 不用在创建的时候检查循环依赖的 beanName 名称
*/
Set<String> inCreationCheckExclusions =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
* 收集一些并不影响主流程的异常,可用于后续再次抛出的时候做一些关联,或者只是收集而不抛出
*/
@Nullable
Set<Exception> suppressedExceptions;
/**
* 表明是否正处于一个正在销毁 singleton 的过程
*/
boolean singletonsCurrentlyInDestruction = false;
/**
* beanName:需要执行destroyMethod 的bean
*/
Map<String, Object> disposableBeans = new LinkedHashMap<>();
/**
* * key: 外部的 beanName
* value 外部 bean 依赖的一些内部 bean
*/
Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
/**
* key bean name
* value 所有依赖 key的 bean
*/
Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
/**
* key bean name
* value 这个key 所依赖的bean
*/
Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
上面除了三级缓存以外,还有其他一些属性定义
getSingletonMutex
我们再来看看其如何实现 SingletonBeanRegistry
接口的方法
@Override
public final Object getSingletonMutex() {
return this.singletonObjects;
}
返回第一级缓存这个 Map
作为同步锁的对象,子类需要使用 synchronized
的时候就要获取这个同步锁对象
registerSingleton
@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "Bean name must not be null");
Assert.notNull(singletonObject, "Singleton object must not be null");
synchronized (this.singletonObjects) {
Object oldObject = this.singletonObjects.get(beanName);
if (oldObject != null) {
throw new IllegalStateException("xxxxx");
}
addSingleton(beanName, singletonObject);
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 加入到第一级缓存
this.singletonObjects.put(beanName, singletonObject);
// 从第三级缓存中移除
this.singletonFactories.remove(beanName);
// 从第二级缓存中移除
this.earlySingletonObjects.remove(beanName);
// 注册这个beanName
this.registeredSingletons.add(beanName);
}
}
先是对参数的检验、然后使用同步锁,再判断该 beanName
是否已经在第一级缓存中、如果已经存在了、则抛出异常,不管在缓存中的 bean
是否跟参数中的 singletonObject
是同一个对象
然后加入到第一级缓存中并注册这个 beanName
、然后从第二级第三级缓存中移除这个 beanName
getSingleton
@Override
@Nullable
public Object getSingleton(String beanName) {
// allowEarlyReference 可以返回第三级缓存的对象
return getSingleton(beanName, true);
}
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// 这个bean 正处于 创建阶段
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 并发控制
synchronized (this.singletonObjects) {
// 单例缓存是否存在
singletonObject = this.earlySingletonObjects.get(beanName);
// 是否运行获取 bean factory 创建出的 bean
if (singletonObject == null && allowEarlyReference) {
// 获取缓存中的 ObjectFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 将对象缓存到 earlySingletonObject中
this.earlySingletonObjects.put(beanName, singletonObject);
// 从工厂缓冲中移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
上面这个方法我们已经见过了很多次 , 分别从第一级而后第二级、再到第三级获取 bean
、如果从第三级的话、那么就将其升级到第二级并从第三级移除。
其他
下面这三个方法就比较简单了、不做介绍了、相信大家都能看一眼就知道干啥了
@Override
public boolean containsSingleton(String beanName) {
return this.singletonObjects.containsKey(beanName);
}
@Override
public String[] getSingletonNames() {
synchronized (this.singletonObjects) {
return StringUtils.toStringArray(this.registeredSingletons);
}
}
@Override
public int getSingletonCount() {
synchronized (this.singletonObjects) {
return this.registeredSingletons.size();
}
}
我们再来看一个往第三级缓存中存放 ObjectFactory
的实现
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
我们再来看看注册 DisposableBean
的方法吧
public void registerDisposableBean(String beanName, DisposableBean bean) {
synchronized (this.disposableBeans) {
this.disposableBeans.put(beanName, bean);
}
}
而执行 destroy
的话内容比较长
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
Set<String> dependencies;
synchronized (this.dependentBeanMap) {
// 找出所有依赖这个 beanName 的其他 bean
dependencies = this.dependentBeanMap.remove(beanName);
}
if (dependencies != null) {
for (String dependentBeanName : dependencies) {
// 最终还是回调到这个方法
destroySingleton(dependentBeanName);
}
}
// 如果 DisposableBean 不为null
if (bean != null) {
try {
bean.destroy();
} catch (Throwable ex) {
}
}
// 处理内部 bean 了
Set<String> containedBeans;
synchronized (this.containedBeanMap) {
// 找出这个 beanName 的所有内部 bean
containedBeans = this.containedBeanMap.remove(beanName);
}
if (containedBeans != null) {
for (String containedBeanName : containedBeans) {
destroySingleton(containedBeanName);
}
}
// dependentBeanMap key 为被依赖者、value 为依赖 key 的 bean
// 这一步的操作就是因为 beanName 可能存在 别人的 value 中、这个时候我们也要去清理掉
// 第一步的时候已经清除了 key 为 beanName 的情况
synchronized (this.dependentBeanMap) {
for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Set<String>> entry = it.next();
Set<String> dependenciesToClean = entry.getValue();
dependenciesToClean.remove(beanName);
if (dependenciesToClean.isEmpty()) {
it.remove();
}
}
}
// dependenciesForBeanMap key 为依赖者,value 为 key 依赖的 bean 集合
this.dependenciesForBeanMap.remove(beanName);
}
FactoryBeanRegistrySupport
我们在来看看这个类,这个类是干啥的
当我们向 Spring
注册的 bean
是 FactoryBean
的话、那么这个 FactoryBean
当然是存放在三级缓存中啦、但是这个 FactoryBean
产生的 bean
也得有个地方缓存起来吧(如果是个单例的话,是吧)
/**
* beanName: bean(factory bean 创建出来的)
* Cache of singleton objects created by FactoryBeans: FactoryBean name to object.
*/
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
我们这里介绍一个上几篇文章说过的一个方法、但是其中的妙处、我现在算是看懂了
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
// 为单例模式且 beanName 已经注册了在 Spring 中
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 从缓存中获取指定的 bean(这个bean 是从 factory bean 创建出来的)
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 为空则从 factory bean 中获取对象
object = doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
// 已经存放到 缓存中了、后续的操作就不需要了
object = alreadyThere;
} else {
// 需要做一些后置处理
if (shouldPostProcess) {
// 如果这个bean正在创建中、
if (isSingletonCurrentlyInCreation(beanName)) {
// Temporarily return non-post-processed object, not storing it yet..
return object;
}
// 前置处理 主要是将这个bean 加入到正在创建中的队列 singletonsCurrentlyInCreation
beforeSingletonCreation(beanName);
try {
// 对 从 factoryBean 获取的对象进行后处理
// 生成对象将暴露给 bean 引用 并回调 beanPostProcessor
object = postProcessObjectFromFactoryBean(object, beanName);
} catch (Throwable ex) {
throw new BeanCreationException(beanName,
"Post-processing of FactoryBean's singleton object failed", ex);
} finally {
// 后置处理 将其从 singletonsCurrentlyInCreation 移除
afterSingletonCreation(beanName);
}
}
// 他的 factory bean 已经存在 缓存中了、那么这个 factory bean 产生的bean 应该也要缓存一下
if (containsSingleton(beanName)) {
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
} else {
// 非单例
.......
}
}
其实有个挺让人迷惑的一个地方
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 为空则从 factory bean 中获取对象
object = doGetObjectFromFactoryBean(factory, beanName);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {
// 已经存放到 缓存中了、后续的操作就不需要了
object = alreadyThere;
}
我上一步已经判断了 factoryBeanObjectCache
里面没有这个 beanName
了,而 doGetObjectFromFactoryBean
这个方法只是单纯的去调用 FactoryBean
里面的 getObject
方法、并无其他操作,那么为啥 alreadyThere
它会有不为 null
的情况呢 ?
我们先设定、FactoryBean
的 beanName
为 beanA
吧,还有一个普通的 beanB
、它是依赖 beanA
的。
那么假如我在 beanA
的 getObject
的方法里面调用 getBean
方法获取 beanB
, 这个时候就构成了一个循环依赖,当创建好 beanB
的时候、进行属性注入,发现要 beanA
、这个时候就会继续走上面的流程、也就是 alreadyThere == null
的情况、这个时候会将 beanA
放置到 factoryBeanObjectCache
中、最终创建好了 beanB
, 返回到 doGetObjectFromFactoryBean
这里的方法、这个时候就会产生了两个 beanA
(如果你正常的在 getObject
new
某个对象的话) , 是不是就出现了 alreadyThere
不为 null
的情况了
来个 demo
看看吧
public class CatFactoryBean implements FactoryBean<Cat>, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public Cat getObject() throws Exception {
beanFactory.getBean("chicken");
return new Cat();
}
@Override
public Class<?> getObjectType() {
return Cat.class;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
public class Chicken {
private Cat cat;
public Chicken() {
}
public void destroyMethod() {
System.out.println("destroy method");
}
public void setCat(Cat cat) {
this.cat = cat;
}
}
public static void main(String[] args) {
Resource resource = new ClassPathResource("coderLi.xml");
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
xmlBeanDefinitionReader.loadBeanDefinitions(resource);
Object cat = defaultListableBeanFactory.getBean("cat");
defaultListableBeanFactory.destroySingletons();
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.demo.data.Chicken" id="chicken" destroy-method="destroyMethod">
<property name="cat" ref="cat"/>
</bean>
<bean class="com.demo.data.CatFactoryBean" id="cat"/>
</beans>
好啦,希望你也有所收获喔
Spring FactoryBean 缓存的更多相关文章
- 转:Spring FactoryBean源码浅析
http://blog.csdn.net/java2000_wl/article/details/7410714 在Spring BeanFactory容器中管理两种bean 1.标准Java Bea ...
- Spring之缓存注解@Cacheable
https://www.cnblogs.com/fashflying/p/6908028.html https://blog.csdn.net/syani/article/details/522399 ...
- 注释驱动的 Spring cache 缓存介绍
概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...
- [转]注释驱动的 Spring cache 缓存介绍
原文:http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ 概述 Spring 3.1 引入了激动人心的基于注释(an ...
- 注释驱动的 Spring cache 缓存介绍--转载
概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...
- Spring实战——缓存
缓存 提到缓存,你能想到什么?一级缓存,二级缓存,web缓存,redis-- 你所能想到的各种包罗万象存在的打着缓存旗号存在的各种技术或者实现,无非都是宣扬缓存技术的优势就是快,无需反复查询等. 当然 ...
- Spring 对缓存的抽象
Cache vs. Buffer A buffer is used traditionally as an intermediate temporary store for data between ...
- 谈谈spring的缓存
缓存到底扮演了什么角色 请移步: http://hacpai.com/article/1376986299174 在对项目进行优化的时候,我们可以主要从以下三个方面入手: 1 缓存 2 集群 3 异 ...
- Java缓存学习之五:spring 对缓存的支持
(注意标题,Spring对缓存的支持 这里不单单指Ehcache ) 从3.1开始,Spring引入了对Cache的支持.其使用方法和原理都类似于Spring对事务管理的支持.Spring Cache ...
随机推荐
- C# Winform学习(六)
目标: 1.树型控件TreeView 2.列表视图控件ListView 一.树型控件 1.命名:tv开始 2.作用:用来显示有层次结构的数据 3.特点 1)允许有多个根节点 2)每个节点可以有多个子节 ...
- Java实现蓝桥杯VIP 算法训练 P0501
试题 算法训练 P0501 资源限制 时间限制:1.0s 内存限制:256.0MB 输入两个无符号整数x, y, 用位操作实现无符号整数的乘法运算.不用考虑整数的溢出. 输入: 235 657 输出: ...
- Java实现蓝桥杯打印图形
标题:打印图形 如下的程序会在控制台绘制分形图(就是整体与局部自相似的图形). 当n=1,2,3的时候,输出如下: 请仔细分析程序,并填写划线部分缺少的代码. n=1时: o ooo o n=2时: ...
- Java实现预排序
1 问题描述 在计算机科学中,预排序是一种很古老的思想.实际上,对于排序算法的兴趣很大程度上是因为这样一个事实:如果列表是有序的,许多关于列表的问题更容易求解.显然,对于包含了排序操作,这种算法的时间 ...
- java实现第三届蓝桥杯源码变换
源码变换 这道题因为有一些html语言在编写的时候不会显示出来,所以就用代码格式把题目写出来 [编程题](满分22分) 超文本标记语言(即HTML),是用于描述网页文档的一种标记语言. HTML通过文 ...
- java实现第六届蓝桥杯打印菱形
打印菱形 给出菱形的边长,在控制台上打印出一个菱形来. 为了便于比对空格,我们把空格用句点代替. 当边长为8时,菱形为: .......* ......*.* .....*...* ....*.... ...
- /etc/alternatives
如何安装一个可执行程序 一般来说我们一个可执行程序,可能在多个路径下,比如在opt路径下,或者在自己的home下. 当要达到在系统的任意路径下敲击该命令,都可执行的话,一般要将该可执行命令的路径加入到 ...
- apollo与springboot集成实现动态刷新配置
分布式apollo简介 Apollo(阿波罗)是携程框架部门研发的开源配置管理中心,能够集中化管理应用不同环境.不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限.流程治理等特性. 本 ...
- STM32串口打印的那些知识
常规打印方法 在STM32的应用中,我们常常对printf进行重定向的方式来把打印信息printf到我们的串口助手.在MDK环境中,我们常常使用MicroLIB+fputc的方式实现串口打印功能,即: ...
- ecshop php商城系统数据库结构及表的介绍分析
ecshop共86张表,确实比较多,不过功能确实最完善的. 表结构分析 ecs_account_log // 用户账目日志表 ecs_activity // 活动表(代码,名称,开始,结束,描述) ...