• Spring 源码(14)Spring Bean 的创建过程(6)对象的提前暴露


    知识回顾

    解析完Bean信息的合并,可以知道Spring在实例化Bean之后,属性填充前,对Bean进行了Bean的合并操作,这里的操作主要做了对Bean对象标记了@Autowired@Value@Resource@PostConstruct@PreDestroy注解的字段或者方法进行解析,主要涉及到类都是BeanPostProcessor的实现,可见BeanPostProcessor接口的重要性。

    这里再次回顾下BeanPostProcessor接口有哪些子接口:

    • InstantiationAwareBeanPostProcessor

      Spring 给机会提前进行实例化,可用通过代理进行对象的创建

    • SmartInstantiationAwareBeanPostProcessor

      用于预测Bean的类型,决定Bean的构造函数,用于实例化

    • MergedBeanDefinitionPostProcessor

      用于合并Bean的信息,即解析Bean对象方法上或者字段上标记的注解,比如@Resource@Autowired@PostConstruct等。

    • DestructionAwareBeanPostProcessor

      用于销毁Bean时调用的,比如执行标有@PreDestroy注解的方法

    这些子接口的实现类比较多,比如:

    • AutowiredAnnotationBeanPostProcessor
    • ComonAnnotationBeanPostProcessor
    • InitDestroyAnnotationBeanPostProcessor
    • AnnotationAwareAspectJAutoProxyCreator
    • ScheduledAnnotationBeanPostProcessor

    当然还不止这里列出来的,还有其他的就不列了,接下来分析Spring源码接下来做了什么?

    对象的提前暴露

    看源码:

    // 省略代码....
    // 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.isTraceEnabled()) {
        logger.trace("Eagerly caching bean '" + beanName +
                     "' to allow for resolving potential circular references");
      }
      // 添加一个lambda表达式到三级缓存中
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    
    // Initialize the bean instance.
    Object exposedObject = bean;
    // 省略代码....
    

    源码这里就添加了一个lambda表达式到一个Map中,然后结束了,并且明确说明了提前暴露是为了解决循环依赖问题。

    什么是循环依赖?

    循环依赖顾名思义,就是你中有我,我中有你,打个比方现在有个对象A,他有个属性b,这个属性b是对象B的,然后对象B中有个属性a,属性a是对象A的。

    现在开始创建对象,按照Spring的标准创建流程getBean-->doGetBean-->createBean-->doCreateBean,先实例化,然后属性填充,然后执行aware方法,然后执行BeanPostProcessorbefore方法,然后执行init-method,然后执行BeanPostProcessorafter方法。那么在执行属性填充时必然会去查找a或者b属性对应的对象,如果找不到就会去创建,那么就会出现下图的样子:

    这样必然就出现了循环依赖,你我紧紧相拥,不想放开,死也要在一起的情形。

    那么Spring为什么解决循环依赖需要进行提前暴露对象呢?

    所以这个问题就很简单了,我们都知道Bean的创建是将实例化和初始化分开的,实例化之后的对象在JVM堆中已经开辟了内存空间地址,这个地址是不会变的,除非山崩地裂,海枯石烂,也就是应用重启了。

    因此可以将已经实例化的对象放在另外一个Map中,一般来说都称之为半成品,当填充属性时,可以将先设置半成品对象,等到对象创建完之后在将半成品换成成品,这样的话对象进行属性填充时就可以直接先使用半成品填充,等到开始初始化时再将对象创建出来即可。

    这样看来循环依赖只需要二级缓存就够了,但是在Spring中,存在一种特殊的对象,就是代理对象。也就是说在放入的半成品我们现在多了一种对象,那就是代理对象,这个时候就会出现使用代理对象还是普通对象呢?所以干脆在搞一个Map专门存放代理对象,这样就区分出来了,然后在使用的时候先判断下我们创建的对象是需要代理还是不需要代理,如果需要代理,那么就创建一个代理对象放在map中,否则直接使用普通对象就可以了。

    Spring中的实现方式

    Spring是怎么处理的呢?Spring是将所有的对象都放在三级缓存中,也就是lambda表达式中:

    // 添加一个lambda表达式到三级缓存中
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    
    
    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);
        }
      }
    }
    

    在属性填充的时候,会执行到getBean,然后从缓存中获取getSingleton

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
      // Quick check for existing instance without full singleton lock
      // 从一级缓存中获取bean实例
      Object singletonObject = this.singletonObjects.get(beanName);
      // 如果一级缓存中没有数据并且没有正在创建的Bean直接返回
      if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 如果有正在创建的Bean,那么冲二级缓存中获取,早期的单例对象
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null && allowEarlyReference) {
          synchronized (this.singletonObjects) {
            // Consistent creation of early reference within full singleton lock
            // 二次检查一级缓存中是否有单例对象
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
              // 二次判断二级缓存中是否存在单例对象
              singletonObject = this.earlySingletonObjects.get(beanName);
              if (singletonObject == null) {
                // 从三级缓存中获取Bean
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                  // 如果三级缓存中 单例工厂中有对象,那么就将该对象放在二级缓存中,并且清掉三级缓存
                  singletonObject = singletonFactory.getObject();
                  this.earlySingletonObjects.put(beanName, singletonObject);
                  this.singletonFactories.remove(beanName);
                }
              }
            }
          }
        }
      }
      return singletonObject;
    }
    

    在获取单例对象时,会执行到三级缓存,然后执行getObject方法,最终就会触发getEarlyBeanReference方法的调用:

    protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
      Object exposedObject = bean;
      // 添加 三级缓存,判断是否需要进行代理创建对象,是一个动态代理创建的代理对象
      if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
          if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
          }
        }
      }
      return exposedObject;
    }
    

    这里会判断,如果这个BeanDefinition是否满足条件,如果不满足,那么直接返回了,否则就会执行到for循环中的代码,而getEarlyBeanReference方法在Spring中只有AbstractAutoProxyCreator类进行了实质的实现:

    public Object getEarlyBeanReference(Object bean, String beanName) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      // 早期代理对象的引用集合
      this.earlyProxyReferences.put(cacheKey, bean);
      // 创建代理
      return wrapIfNecessary(bean, beanName, cacheKey);
    }
    

    点进去:

    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
      if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        return bean;
      }
      if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
      }
      if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
      }
    
      // Create proxy if we have advice.
      Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
      if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建代理对象
        Object proxy = createProxy(
          bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
      }
    
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
    }
    

    首先进行了判断,如果不满足创建代理的条件,都是直接返回这个对象,否则进入创建代理的方法,创建出代理对象,最终放入缓存中。点入到最后会发现使用了两种代理创建方式:

    源码中的提前暴露对象牵扯出很多东西,循环依赖,三级缓存,aop等,这里解析了个大概,接下来继续主流程中的属性填充populaeBean方法。

  • 相关阅读:
    百度指数 Cipher-Text、百度翻译 Acs-Token 逆向分析
    HTTP/1.1,HTTP/2.0和HTTP/3.0 各版本协议的详解(2024-04-24)
    用友U8-OA getSessionList.jsp信息泄露 复现
    CSDN博客专家
    折扇-第10届蓝桥杯Scratch省赛真题第4题
    详细了解JVM运行时内存
    机器学习笔记:初始化0的问题
    Workflow,要不要了解一下
    Mybatis一对多关联查询,返回值Map,字段自动映射
    中国国债发行数据集(2002-2023)
  • 原文地址:https://www.cnblogs.com/redwinter/p/16286745.html