Spring FactoryBean 缓存
Posted CoderLi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 缓存的主要内容,如果未能解决你的问题,请参考以下文章