• Spring读书笔记——bean创建(下)


    有关Spring加载bean系列,今天这是最后一篇了,主要接上篇对于从Spring容器中获取Bean的一些细节实现的补充。

    • 《Spring读书笔记——bean加载》——Spring如何加载消化一个xml配置文件

    • 《Spring读书笔记——bean解析》——Spring如何将xml文件的各种标签转换为BeanDefinition并注册到Spring容器下

    • 《Spring读书笔记——bean创建(上)》——概述Spring如何从容器中取出需要的那个Bean

    从缓存中加载单例

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
       Object singletonObject = this.singletonObjects.get(beanName);
       if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
          synchronized (this.singletonObjects) {
             singletonObject = this.earlySingletonObjects.get(beanName);
             if (singletonObject == null && allowEarlyReference) {
                ObjectFactory singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                   singletonObject = singletonFactory.getObject();
                   this.earlySingletonObjects.put(beanName, singletonObject);
                   this.singletonFactories.remove(beanName);
                }
             }
          }
       }
       return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
    

    在看这段代码之前,我们先了解下Spring对于单例bean出现循环依赖的解决方法。如果出现上面我们介绍的A->B->C->A的情况,那是不是说Spring就无能为力了,显然Spring没有那么弱。那么Spring是怎么做的?
    鉴于单例bean的循环依赖问题,Spring创建bean的原则是不等bean创建完成就会将bean的ObjectFactory提前曝光加入到缓存中,一旦有某个bean创建时需要依赖这个bean了,那么就可以直接使用ObjectFactory。
    简单说,创建bean的时候,就是打包快递发货,主管为了知道你今天要派发多少个包裹,为了节省大家时间以及以免统计漏掉的情况。你可以先拿出一个包裹箱子,上面写上要寄收件人、收货地址、联系方式等等,但是这时候还没有往里面打包真正的快递。
    这里曝光的bean就相当于这个快递箱子。

    好了,知道了这个原则之后,我们就好理解代码了。
    首先从singletonObjects中获取实例,取不到则从earlySingletonObjects中获取,仍然取不到,我们还可以到singletonFactories中获取相应的ObjectFactory,在调用这个ObjectFactory的getObject方法来创建bean。
    然后将其加入到earlySingletonObjects中,在将其从singletonFactories中删除。

    想必,你已经被这些用来存储和删除的集合搞疯了,没关系,我们来理一下:

    • singletonObjects
    /** Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
    

    用于保存BeanName和创建bean实例之间的关系。

    • singletonFactories:
    /** Cache of singleton factories: bean name --> ObjectFactory */
    private final Map<String, ObjectFactory> singletonFactories = new HashMap<String, ObjectFactory>(16);
    

    用于保存BeanName和创建bean的工厂之间的关系

    • earlySingletonObjects:
    /** Cache of early singleton objects: bean name --> bean instance */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
    

    用于保存BeanName和创建bean的工厂之间的关系,与singletonObjects的区别是当一个bean被放入这个集合后,可以用于其他bean做循环依赖检查

    bean实例化

    我们从缓存中拿到bean之后,就需要根据bean的不同类型做不同的处理,返回相应的bean,实现这个功能的就是getObjectForBeanInstance方法

    protected Object getObjectForBeanInstance(
    			Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
    
    		// Don't let calling code try to dereference the factory if the bean isn't a factory.
    		if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
    			throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
    		}
    
    		// Now we have the bean instance, which may be a normal bean or a FactoryBean.
    		// If it's a FactoryBean, we use it to create a bean instance, unless the
    		// caller actually wants a reference to the factory.
    		if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
    			return beanInstance;
    		}
    
    		Object object = null;
    		if (mbd == null) {
    			object = getCachedObjectForFactoryBean(beanName);
    		}
    		if (object == null) {
    			// Return bean instance from factory.
    			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
    			// Caches object obtained from FactoryBean if it is a singleton.
    			if (mbd == null && containsBeanDefinition(beanName)) {
    				mbd = getMergedLocalBeanDefinition(beanName);
    			}
    			boolean synthetic = (mbd != null && mbd.isSynthetic());
    			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    		}
    		return object;
    	}
    
    • 首先检测指定的beanName是否是工厂bean相关,如果既不是工厂bean又是以"&"开头,则校验失败,抛出异常
    • 如果这个bean不是工厂Bean(FactoryBean),那么就直接返回bean实例
    • 剩下代码就是处理FactoryBean,我们顺着这样的顺序依次来到getObjectForBeanInstance->getObjectFromFactoryBean->doGetObjectFromFactoryBean
    private Object doGetObjectFromFactoryBean(
    			final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
    			throws BeanCreationException {
    
    		Object object;
    		try {
    			if (System.getSecurityManager() != null) {
    				AccessControlContext acc = getAccessControlContext();
    				try {
    					object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
    						public Object run() throws Exception {
    								return factory.getObject();
    							}
    						}, acc);
    				}
    				catch (PrivilegedActionException pae) {
    					throw pae.getException();
    				}
    			}
    			else {
    				object = factory.getObject();
    			}
    		}
    		catch (FactoryBeanNotInitializedException ex) {
    			throw new BeanCurrentlyInCreationException(beanName, ex.toString());
    		}
    		catch (Throwable ex) {
    			throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
    		}
    
    
    		// Do not accept a null value for a FactoryBean that's not fully
    		// initialized yet: Many FactoryBeans just return null then.
    		if (object == null && isSingletonCurrentlyInCreation(beanName)) {
    			throw new BeanCurrentlyInCreationException(
    					beanName, "FactoryBean which is currently in creation returned null from getObject");
    		}
    
    		if (object != null && shouldPostProcess) {
    			try {
    				object = postProcessObjectFromFactoryBean(object, beanName);
    			}
    			catch (Throwable ex) {
    				throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);
    			}
    		}
    
    		return object;
    	}
    

    这么长的代码,如果嫌累,就只看factory.getObject()这行就好,这诠释了FactoryBean的加载时通过factory.getObject的方式获取到对应的bean实例的。

    如何创建单例bean

    在上篇的doGetBean方法中,如果从缓存中加载不到,那么我们就需要老老实实的从头开始加载bean了,对于单例bean的加载就都在这里实现了

    // Create bean instance.
    			if (mbd.isSingleton()) {
    				sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
    					public Object getObject() throws BeansException {
    						try {
    							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.
    							destroySingleton(beanName);
    							throw ex;
    						}
    					}
    				});
    				bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    			}
    

    创建bean

    我们从AbstractBeanFactory的createBean方法来到了AbstractAutowiredCapableBeanFactory的createbean方法,而真正的创建bean其实在doCreateBean方法中

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
       // Instantiate the bean.
       BeanWrapper instanceWrapper = null;
       if (mbd.isSingleton()) {
          instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
       }
       if (instanceWrapper == null) {
          instanceWrapper = createBeanInstance(beanName, mbd, args);
       }
       final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
       Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
    
       // Allow post-processors to modify the merged bean definition.
       synchronized (mbd.postProcessingLock) {
          if (!mbd.postProcessed) {
             applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
             mbd.postProcessed = true;
          }
       }
    
       // Eagerly cache singletons to be able to resolve circular references
       // even when triggered by lifecycle interfaces like BeanFactoryAware.
       boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
             isSingletonCurrentlyInCreation(beanName));
       if (earlySingletonExposure) {
          if (logger.isDebugEnabled()) {
             logger.debug("Eagerly caching bean '" + beanName +
                   "' to allow for resolving potential circular references");
          }
          addSingletonFactory(beanName, new ObjectFactory() {
             public Object getObject() throws BeansException {
                return getEarlyBeanReference(beanName, mbd, bean);
             }
          });
       }
    
       // Initialize the bean instance.
       Object exposedObject = bean;
       try {
          populateBean(beanName, mbd, instanceWrapper);
          if (exposedObject != null) {
             exposedObject = initializeBean(beanName, exposedObject, mbd);
          }
       }
       catch (Throwable ex) {
          if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
             throw (BeanCreationException) ex;
          }
          else {
             throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
          }
       }
    
       if (earlySingletonExposure) {
          Object earlySingletonReference = getSingleton(beanName, false);
          if (earlySingletonReference != null) {
             if (exposedObject == bean) {
                exposedObject = earlySingletonReference;
             }
             else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                   if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                      actualDependentBeans.add(dependentBean);
                   }
                }
                if (!actualDependentBeans.isEmpty()) {
                   throw new BeanCurrentlyInCreationException(beanName,
                         "Bean with name '" + beanName + "' has been injected into other beans [" +
                         StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                         "] in its raw version as part of a circular reference, but has eventually been " +
                         "wrapped. This means that said other beans do not use the final version of the " +
                         "bean. This is often the result of over-eager type matching - consider using " +
                         "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
                }
             }
          }
       }
    
       // Register bean as disposable.
       try {
          registerDisposableBeanIfNecessary(beanName, bean, mbd);
       }
       catch (BeanDefinitionValidationException ex) {
          throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
       }
    
       return exposedObject;
    }
    
    • 如果是单例Bean,那么首先是从factoryBeanInstanceCache中清除该beanName对应的记录
    • 实例化bean,将BeanDefinition转换为BeanWrapper对象
    • bean合并后的处理
    • 解决循环依赖问题
    • 属性填充,将所有属性填充到bean的实例中
      这个方法,本身不算长,但是层层深入就会发现其下面包罗了创建bean的诸多繁杂的细节(这块自己看的也是云里雾里,就不往下延伸扩展了)。

    虽然对于Spring加载bean,我只写了四篇,但是其内部实现远比我表述的要复杂的多。
    看源码确实很煎熬,对于目前看不懂的地方要么多看几遍,要么先跳过。阅读代码的过程中要懂得取舍,对于非重点部分比如日志或者异常处理可以先忽略,沿着一条主线往下看,最主要是先弄懂代码的只要意图。
    Spring的bean加载代码量虽然巨大,但是思路还是比较清晰的,我们知道Spring如何加载xml然后解析xml,再到如何把xml的元素转为自己的BeanDefinition,最后又是如何取出对应的beanName然后返回一个bean实例供容器使用的。

    网上有一位大神用一张图就把整个过程画出来了

    注:图片来源http://blog.csdn.net/zghwaicsdn/article/details/50910384

    如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

  • 相关阅读:
    kindle--瓦尔登湖
    8051
    c++
    Linux安装目录的选择
    Redis键值数据类型之散列类型
    Redis键值数据类型之字符串
    redis基本使用
    Ubuntu18.04 Redis安装
    Java String和int转换
    mysql获得自增主码的值
  • 原文地址:https://www.cnblogs.com/bigdataZJ/p/SpringSource4.html
Copyright © 2020-2023  润新知