• 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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    上面的代码是我们之前手写的解决循环依赖的代码,我们也可以看看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);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    方法的开始是从缓存中获取对象,AbstractBeanFactory中的getSingleton(String beanName)源码如下:

    	@Override
    	@Nullable
    	public Object getSingleton(String beanName) {
    		//在这里 系统一般是允许早期对象引用的  allowEarlyReference 通过控制这个参数可以解决循环依赖
    		return getSingleton(beanName, true);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    这个方法中把锁加在了从二级缓存获取之前并是循环依赖的情况下,从一级缓存获取之后。

    另一个锁在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;
    		}
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    这里面的锁加载Bean创建的前面,整个Bean的创建都在锁里面。

    总结:锁解决了Spring并发引起的读取不完整Bean;二级三级缓存在没有循环依赖的时候是不会用到的;

  • 相关阅读:
    【批处理DOS-CMD命令-汇总和小结】-文件与目录操作命令(md、rd、xcopy、dir、cd、set、move、copy、del、type、sort)
    【密评】商用密码应用安全性评估从业人员考核题库(五)
    【leetcode】【2022/9/12】1608. 特殊数组的特征值
    [附源码]Python计算机毕业设计java视频点播系统
    Java线程池ExecutorService和Executors应用(Spring Boot微服务)
    fail-safe 机制与 fail-fast 机制
    400企业热线电话
    Android数据库处理重复插入Insert数据的问题
    CCNA课程实验-14-Final_Lab
    Mysql创建管理表的内容上的打字练习
  • 原文地址:https://blog.csdn.net/Mao_yafeng/article/details/127848887