相关文章

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

  • 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 415 字符串相加

    415. 字符串相加 给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和. 注意: num1 和num2 的长度都小于 5100. num1 和num2 都只包含数字 0-9. num ...

  2. Java实现 LeetCode 22 括号生成

    22. 括号生成 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合. 例如,给出 n = 3,生成结果为: [ "((()))", &quo ...

  3. Java实现LeetCode_0028_ImplementStrStr

    package javaLeetCode.primary; import java.util.Scanner; public class ImplementStrStr_28 { public sta ...

  4. opencl(7) 内核执行命令入队]工作组、工作项

    1:将内核执行命令入队(该命令可被多个工作项执行) cl_int clEnqueueNDRangeKernel( cl_command_queue command_queue, cl_kernel k ...

  5. iOS-Code Data的快速体验

    Code Data Core Data 是iOS SDK 里的一个很强大的框架,允许程序员以面向对象的方式储存和管理数据.使用Core Data框架,程序员可以很轻松有效地通过面向对象的接口管理数据 ...

  6. Linux文件系统--基于EXT2

    一.文件系统基本知识 ① 存储在永久性存储介质,由程序按照某种格式制作的数据集合叫做文件 ② 磁盘上管理文件的文件.数据结构和操作构成磁盘文件系统,简称文件系统 ③ 文件属性 (1)名称 (2)位置 ...

  7. 02.vue-router的进阶使用

    关键字:路由懒加载,全局导航守卫,组件导航守卫,redirect重定向,keep-alive,params,query 一.目录结构            二.index.js // 配置路由相关的信 ...

  8. Windows下mysql的基础操作

    1.数据库表操作: - 首先启动mysql服务器,在安装mysql的目录下打开cmd窗口,运行mysql:'mysql.exe -hlocalhost -p3306 -uroot -p123456'; ...

  9. Java 多线程基础(三) start() 和 run()

    Java 多线程基础(三) start() 和 run() 通过之前的学习可以看到,创建多线程过程中,最常用的便是 Thread 类中的 start() 方法和线程类的 run() 方法.两个方法都包 ...

  10. 时间序列神器之争:prophet VS lstm

    一.需求背景 我们福禄网络致力于为广大用户提供智能化充值服务,包括各类通信充值卡(比如移动.联通.电信的话费及流量充值).游戏类充值卡(比如王者荣耀.吃鸡类点券.AppleStore充值.Q币.斗鱼币 ...