5.1 Spring源码-读取不完整Bean的解决原理

5.1Spring源码-读取不完整Bean的解决原理

前置背景了解:IOC容器加载过程和Bean循环依赖讲解

Bean循环依赖讲解

1.0 讲解读取不完整Bean场景及解决

BeanFactory 通过getBean方法获取bean对象;getBean方法的逻辑是:

1)从缓存中获取

  • 从一级缓存获取;
  • 若果一级缓存没有并且是循环依赖:从二级缓存获取;
  • 若二级缓存没有,从三级缓存获取;

2)添加循环依赖标识

  1. 实例化Bean,并且创建代理对象,放入三级缓存

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源码中是怎么解决的.

2.0 从源码分析Spring解决读取不完整Bean的原理

下面的源码都是在 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;二级三级缓存在没有循环依赖的时候是不会用到的;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苹水相峰

你的打赏是对我最大的肯定

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值