相关文章

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

  • 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实现 LeetCode 769 最多能完成排序的块(单向遍历)

    769. 最多能完成排序的块 数组arr是[0, 1, -, arr.length - 1]的一种排列,我们将这个数组分割成几个"块",并将这些块分别进行排序.之后再连接起来,使得 ...

  2. Java实现欧拉筛与花里胡哨求质数高级大法的对比

    我也不清楚这是什么高级算法,欧拉筛是昨天有位大佬,半夜无意间告诉我的 欧拉筛: 主要的含义就是我把这个数的所有倍数都弄出来,然后下次循环的时候直接就可以跳过了 import java.text.Sim ...

  3. Java实现 LeetCode 299 猜数字游戏

    299. 猜数字游戏 你正在和你的朋友玩 猜数字(Bulls and Cows)游戏:你写下一个数字让你的朋友猜.每次他猜测后,你给他一个提示,告诉他有多少位数字和确切位置都猜对了(称为"B ...

  4. Java实现 LeetCode 283 移动零

    283. 移动零 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明: 必 ...

  5. Java实现 蓝桥杯 算式最大值

    算式最大值 [问题描述] 给定: 1)N个正整数A1, A2, - AN; 2)P个加号+和Q个减号-; (P+Q=N-1) 3)K对括号() 请你使用全部整数.加减号和括号,组成一个合法的算式(A1 ...

  6. Java实现 LeetCode 25 K个一组翻转链表

    25. K 个一组翻转链表 给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表. k 是一个正整数,它的值小于或等于链表的长度. 如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持 ...

  7. java实现数字的值返回

    以下的静态方法实现了:把串 s 中第一个出现的数字的值返回. 如果找不到数字,返回-1 例如: s = "abc24us43" 则返回 2 s = "82445adb5& ...

  8. Java实现二阶魔方旋转

    魔方可以对它的6个面自由旋转. 我们来操作一个2阶魔方(如图1所示): 为了描述方便,我们为它建立了坐标系. 各个面的初始状态如下: x轴正向:绿 x轴反向:蓝 y轴正向:红 y轴反向:橙 z轴正向: ...

  9. /etc/alternatives

    如何安装一个可执行程序 一般来说我们一个可执行程序,可能在多个路径下,比如在opt路径下,或者在自己的home下. 当要达到在系统的任意路径下敲击该命令,都可执行的话,一般要将该可执行命令的路径加入到 ...

  10. Jmeter让压测随时做起来(转载)

    为什么要压测 这个问题问的其实挺没有必要的,做开发的同学应该都很清楚,压测的必要性,压力测试主要目的就是让我们在上线前能够了解到我们系统的承载能力,和当前.未来系统压力的提升情况,能够评估出当前系统的 ...