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

  • Post author:
  • Post category:其他




🚀 优质资源分享 🚀

学习路线指引(点击解锁) 知识定位 人群定位

🧡 Python实战微信订餐小程序 🧡
进阶级 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。

💛Python量化交易实战💛
入门级 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统



知识回顾

解析完

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

方法,然后执行

BeanPostProcessor



before

方法,然后执行

init-method

,然后执行

BeanPostProcessor



after

方法。那么在执行属性填充时必然会去查找

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

方法。



版权声明:本文为u013190417原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。