相关文章

读完这篇文章你将会收获到

  • DisposableBeandestroy 执行
  • Spring 如何缓存 FactoryBean 产生的单例 bean
  • 如何解决 FctoryBeangetObject 的循环依赖

不多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 注册的 beanFactoryBean 的话、那么这个 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 的情况呢 ?

我们先设定、FactoryBeanbeanNamebeanA 吧,还有一个普通的 beanB、它是依赖 beanA 的。

那么假如我在 beanAgetObject 的方法里面调用 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 缓存的更多相关文章

  1. 转:Spring FactoryBean源码浅析

    http://blog.csdn.net/java2000_wl/article/details/7410714 在Spring BeanFactory容器中管理两种bean 1.标准Java Bea ...

  2. Spring之缓存注解@Cacheable

    https://www.cnblogs.com/fashflying/p/6908028.html https://blog.csdn.net/syani/article/details/522399 ...

  3. 注释驱动的 Spring cache 缓存介绍

    概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...

  4. [转]注释驱动的 Spring cache 缓存介绍

    原文:http://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/ 概述 Spring 3.1 引入了激动人心的基于注释(an ...

  5. 注释驱动的 Spring cache 缓存介绍--转载

    概述 Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使 ...

  6. Spring实战——缓存

    缓存 提到缓存,你能想到什么?一级缓存,二级缓存,web缓存,redis-- 你所能想到的各种包罗万象存在的打着缓存旗号存在的各种技术或者实现,无非都是宣扬缓存技术的优势就是快,无需反复查询等. 当然 ...

  7. Spring 对缓存的抽象

    Cache vs. Buffer A buffer is used traditionally as an intermediate temporary store for data between ...

  8. 谈谈spring的缓存

    缓存到底扮演了什么角色 请移步:  http://hacpai.com/article/1376986299174 在对项目进行优化的时候,我们可以主要从以下三个方面入手: 1 缓存 2 集群 3 异 ...

  9. Java缓存学习之五:spring 对缓存的支持

    (注意标题,Spring对缓存的支持 这里不单单指Ehcache ) 从3.1开始,Spring引入了对Cache的支持.其使用方法和原理都类似于Spring对事务管理的支持.Spring Cache ...

随机推荐

  1. Java实现 蓝桥杯油漆问题

    标题:油漆面积 X星球的一批考古机器人正在一片废墟上考古. 该区域的地面坚硬如石.平整如镜. 管理人员为方便,建立了标准的直角坐标系. 每个机器人都各有特长.身怀绝技.它们感兴趣的内容也不相同. 经过 ...

  2. Java实现 LeetCode 668 乘法表中第k小的数(二分)

    668. 乘法表中第k小的数 几乎每一个人都用 乘法表.但是你能在乘法表中快速找到第k小的数字吗? 给定高度m .宽度n 的一张 m * n的乘法表,以及正整数k,你需要返回表中第k 小的数字. 例 ...

  3. Java实现 LeetCode 23 合并K个排序链表

    23. 合并K个排序链表 合并 k 个排序链表,返回合并后的排序链表.请分析和描述算法的复杂度. 示例: 输入: [ 1->4->5, 1->3->4, 2->6 ] 输 ...

  4. cnblogs 博客爬取 + scrapy + 持久化 + 分布式

    目录 普通 scrapy 分布式爬取 cnblogs_spider.py 普通 scrapy # -*- coding: utf-8 -*- import scrapy from ..items im ...

  5. Cypress系列(22)- 可操作类型的命令 之 select()

    如果想从头学起Cypress,可以看下面的系列文章哦 https://www.cnblogs.com/poloyy/category/1768839.html .select() 在 <sele ...

  6. LinkedList竟然比ArrayList慢了1000多倍?(动图+性能评测)

    数组和链表是程序中常用的两种数据结构,也是面试中常考的面试题之一.然而对于很多人来说,只是模糊的记得二者的区别,可能还记得不一定对,并且每次到了面试的时候,都得把这些的概念拿出来背一遍才行,未免有些麻 ...

  7. 搭建redis哨兵模式

    搭建redis哨兵模式,一主两从三哨兵   1.从官网下载redis安装包:此处是redis-5.0.7.tar.gz 2.上传到目录 /utxt/soft 3.解压 4.cd /utxt/soft/ ...

  8. Spring Cloud 系列之 Dubbo RPC 通信

    Dubbo 介绍 官网:http://dubbo.apache.org/zh-cn/ Github:https://github.com/apache/dubbo 2018 年 2 月 15 日,阿里 ...

  9. Win10搭建VM12.0.1虚拟机,虚拟机网络同宿主机ping不通的解决办法

    准备系统学习Linux系统,在电脑搭建了一个CentOS虚拟机,希望能从宿主机连接至虚拟机. 尝试了很多办法,碰到各种坑,最后这个方法成功了! 分享给大家,希望有所帮助. 一.环境 1.宿主机:Win ...

  10. JVM之HotSpot虚拟机是如何发起内存回收的?

    1.背景 在上一节中,我们掌握了垃圾收集的一些算法,也弄明白了分代回收的原理, 那么HotSpot虚拟机是如何发起内存回收的? 2.如何找到GC Roots根节点(枚举根节点) 从可达性分析中GC R ...