5.1Spring源码-读取不完整Bean的解决原理
前置背景了解:IOC容器加载过程和Bean循环依赖讲解
Bean循环依赖讲解
BeanFactory 通过getBean方法获取bean对象;getBean方法的逻辑是:
1)从缓存中获取
2)添加循环依赖标识
4)Bean属性赋值
5)初始化Bean,添加一级缓存
如果线程一和线程二同时调用getBean(A),当线程一执行到了3) 实例化Bean,并且创建代理对象,放入三级缓存这一步,另一对象这时走从缓存中获取对象,这时A对象在三级缓存中并且是不完整的Bean(属性未赋值),
哪我们怎么解决并发引起的读取不完整的Bean的问题呢?
想要达到的效果如下:
线程一 getBean(A)–>加锁–>Bean声明周期–>addSingleton 加入到一级缓存、移除二三级缓存
线程二 getBean(A)–>getSingleton锁-----------------阻塞------------
这样我们就不会从缓存中获取不完整的Bean了,需要在getBean 一开始的时候添加锁;哪我们需要在实例化之前添加锁,哪锁的地方如下:1) getSingleton从缓存中获取;2)在getBean方法的实例化之前,代码如下
//获取Bean
public static Object getBean(String beanName) throws Exception{
Object singleton = getSingleton(beanName);
if(singleton!=null){
return singleton;
}
Object instanceBean = null;
synchronized (singletonObject) {
//正在创建
if(!singletonCurrentlyInCreation.contains(beanName)){
singletonCurrentlyInCreation.add(beanName);
}
//1.实例化
RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionMap.get(beanName);
Class<?> beanClass = beanDefinition.getBeanClass();
instanceBean = beanClass.newInstance();
//如果添加到一级缓存 (解决了对象依赖循环但是对象不完整) 并发场景量同时getBean 可能拿到不完整的Bean
// 创建动态代理 (耦合方式,BeanPostProcessor) Spring 还是希望正常的bean 还是在初始化后创建
//只有在循环依赖的情况下在实例化后创建proxy
Object finalInstanceBean = instanceBean;
singletonFactories.put(beanName, new ObjectFactory() {
@Override
public Object getObject() throws BeansException {
Object earlyBeanReference = new JdkProxyBeanPostProcessor().getEarlyBeanReference(finalInstanceBean, beanName);
return earlyBeanReference;
}
});
//添加二级缓存
// earlySingletonObject.put(beanName,instanceBean);
//2.属性赋值
Field[] declaredFields = beanClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
Autowired annotation = declaredField.getAnnotation(Autowired.class);
//说明属性上面有@Autowired注解
if (annotation != null) {
declaredField.setAccessible(true);
//byName byType byConstrator
//BeanB
String name = declaredField.getName();
//获取BeanB对象
Object b = getBean(name);
//设置BeanA对象的BeanB属性
declaredField.set(instanceBean, b);
}
}
//3.初始化
//(2.4)如果是动态代理一级缓存中应该也是
if (earlySingletonObject.containsKey(beanName)) {
instanceBean = earlySingletonObject.get(beanName);
}
//4.添加到一级缓存 (解决了对象依赖循环但是对象不完整) 并发场景量同时getBean 可能拿到不完整的Bean
singletonObject.put(beanName, instanceBean);
}
return instanceBean;
}
//从一级缓存获取
public static Object getSingleton(String beanName) throws IllegalAccessException, InstantiationException {
Object bean = singletonObject.get(beanName);
//一级缓存没有就说明是循环依赖
if(bean==null&&singletonCurrentlyInCreation.contains(beanName)){
synchronized (singletonObject) {
//判断二级缓存是否有
bean = earlySingletonObject.get(beanName);
//二级缓存没有从三级缓存拿
if (bean == null) {
//从三级缓存拿
ObjectFactory factory = singletonFactories.get(beanName);
if (factory != null) {
//放入二级缓存
earlySingletonObject.put(beanName, factory.getObject());
}
}
}
}
return bean;
}
上面的代码是我们之前手写的解决循环依赖的代码,我们也可以看看Spring源码中是怎么解决的.
下面的源码都是在 AbstractBeanFactory.java和DefaultSingletonBeanRegistry.java 中。
AbstractBeanFactory中的getBean():从Bean工厂中获取Bean对象源码如下
@Override
public Object getBean(String name) throws BeansException {
//真正获取Bean的逻辑
return doGetBean(name, null, null, false);
}
AbstractBeanFactory中的doGetBean() 里面是getBean()的具体实现源码(部分主要代码)如下
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
//这里传入的name 可能是别名,也有可能是工厂的Bean的name,所以需要在这里转换
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
//尝试去缓存中获取对象
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// Create bean instance.
//创建单例Bean
if (mbd.isSingleton()) {
//把BeanName 和一个singletonFactory 传入一个回调对象用于回调
//这个getSingleton和上面的getSingleton不是一个方法。
sharedInstance = getSingleton(beanName, () -> {
try {
//进入创建Bean的逻辑
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
// 创建Bean的过程中发生异常,需要销毁关于当前Bean的所有信息
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
return (T) bean;
}
方法的开始是从缓存中获取对象,AbstractBeanFactory中的getSingleton(String beanName)源码如下:
@Override
@Nullable
public Object getSingleton(String beanName) {
//在这里 系统一般是允许早期对象引用的 allowEarlyReference 通过控制这个参数可以解决循环依赖
return getSingleton(beanName, true);
}
AbstractBeanFactory中的getSingleton(beanName, true)源码如下:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
/**
* 第一步:尝试去一级缓存获取(单例缓存池中获取对象,一般情况下从该map中获取的对象可以直接使用)
* IOC容器初始化加载单例Bean的时候,第一次进来的时候 该map中一般返回时空
*/
Object singletonObject = this.singletonObjects.get(beanName);
/**
* 若在第一级缓存中没有获取到对象,并且singletonCurrentlyInCreation这个list包含改beanName
* IOC容器初始化加载单实例Bean的时候第一次进来的时候,该list中一般返回空,但是在循环依赖的时候返回时true
*/
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
/**
* 尝试去二级缓存获取对象(二级缓存中的对象是一个早期对象)
* 早期对象:就是Bean刚刚调用构造方法,还来不及给Bean的属性赋值的对象(纯净对象)
*/
singletonObject = this.earlySingletonObjects.get(beanName);
/**
* 二级缓存中也没有获取到对象,allowEarlyReference 为true 为循环依赖(参数是上个方法传过来的)
*/
if (singletonObject == null && allowEarlyReference) {
/**
* 直接从三级缓存中获取,ObjectFactory对象 这个对接就是用来解决循环依赖的关键所在
* 在IOC 后期的过程中,当Bean调用构造方法的时候,把早期对象包裹成一个ObjectFactory对象
* 暴露在三级缓存中
*/
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
//从三级缓存中获取的对象不为空
if (singletonFactory != null) {
/**
* 在这里通过暴露ObjectFactory包装对象,在通过调用它的getObject来获取纯净对象
* 在这个缓解中调用到 getEarlyReference来进行后置处理
*/
singletonObject = singletonFactory.getObject();
//把早期对象放置在二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
//ObjectFactory 包装对象从三级缓存中删除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
这个方法中把锁加在了从二级缓存获取之前并是循环依赖的情况下,从一级缓存获取之后。
另一个锁在AbstractBeanFactory中的doGetBean() 的下面的getSingleton方法(DefaultSingletonBeanRegistry类)中源码如下
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
/**
* 加锁---这里非常关键
* 为什么Spring能够避免多线程下读取到不完整的Bean
* 因为在创建之前锁住一级缓存,知道添加到一级缓存中
*/
synchronized (this.singletonObjects) {
/** 为什么在这里在从单例池再拿一次?因为多线程下
* 线程一 getBean(A)-->加锁-->Bean声明周期-->addSingleton 加入到一级缓存、移除二三级缓存
* 线程二 getBean(A)-->getSingleton锁-----------------阻塞------------
*/
// <1>尝试送单例缓存池中获取对象
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
/**
* <2>标记当前的Bean马上就要被创建了
* singletonCurrentlyInCreation 在这里会把beanName加入进来,若第二次循环依赖
*/
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
/**
* <3>初始化Bean
* 这个过程其实就是调用createBean() 方法 -->doCreateBean(Bean实例化,属性赋值,初始化)
*/
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
//回调singletonObjects的get方法,进行正在创建Bean的逻辑
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
/**
* <4>后置处理
* 主要做的事情就是把singletonCurrentlyInCreation 标记正在创建Bean从集合中移除
*/
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
这里面的锁加载Bean创建的前面,整个Bean的创建都在锁里面。
总结:锁解决了Spring并发引起的读取不完整Bean;二级三级缓存在没有循环依赖的时候是不会用到的;