Spring源码之bean 的加载(二)

简介: bean 的加载(二)

bean 的加载(二)


之前文章主要对 getBean 方法进行简单的介绍,和 FactoryBean 的作用,以及是如何从缓存中获取 bean。本文继续讲解 bean 的加载流程。


从 bean 的实例中获取对象

在 getBean 方法里,getObjectForBeanInstance()是个常用的方法,无论是从缓存中获取 bean 还是根据不同 scope 策略来加载 bean。总而言之,我们在获取到 bean 实例后第一步就是调用这个方法来检测正确性,其实就是检测当前 bean 是否为 FactoryBean 类型的 bean,如果是则调用 FactoryBean 实例的 getObject()作为返回值。


需要注意的是无论是缓存中获取到的 bean 还是通过 scope 策略加载的 bean 都是原始的 bean 状态,我们需要的是工厂 bean 中定义 factory-method 方法中返回的 bean,而 getObjectForBeanInstance 方法就是完成这个工作的。

protected Object getObjectForBeanInstance(
      Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
   //如果指定的name是工厂相关以"&"为前缀,并且beanInstance又不是FactoryBean则校验不通过
   if (BeanFactoryUtils.isFactoryDereference(name)) {
      if (beanInstance instanceof NullBean) {
         return beanInstance;
      }
      if (!(beanInstance instanceof FactoryBean)) {
         throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
      }
      if (mbd != null) {
         mbd.isFactoryBean = true;
      }
      return beanInstance;
   }
   //现在我们有了这个bean实例,这个实例可能是FactoryBean,也可能是正常的bean
   //如果是FactoryBean的话,我们使用它创建实例,但如果是用户想要直接获取工厂实例而不是工厂的getObject方法则需要在BeanName前加上"&"
   if (!(beanInstance instanceof FactoryBean)) {
      return beanInstance;
   }
   //加载FactoryBean
   Object object = null;
   if (mbd != null) {
      mbd.isFactoryBean = true;
   }
   else {
      //尝试从缓存中加载bean
      object = getCachedObjectForFactoryBean(beanName);
   }
   if (object == null) {
      // 执行到这里表明beanInstance一定是一个FactoryBean
      FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
      // containsBeanDefinition检测BeanDefinitionMap中也就是在所有一键加载的类中检测是否定义beanName
      if (mbd == null && containsBeanDefinition(beanName)) {
         //将GenericBeanDefinition转换为RootBeanDefinition,如果指定BeanName是子bean的话同时会合并父类的属性
         mbd = getMergedLocalBeanDefinition(beanName);
      }
      //是否是用户定义的而不是应用程序本身定义的
      boolean synthetic = (mbd != null && mbd.isSynthetic());
      object = getObjectFromFactoryBean(factory, beanName, !synthetic);
   }
   return object;
}

我们先看一下 getObjectForBeanInstance 主要做了什么:


  • 对 FactoryBean 正确性进行验证
  • 对非 FactoryBean 不作任何处理
  • 对 bean 进行转换
  • 将从 Factory 中解析 bean 的工作委托给了getObjectFromFactoryBean


该段代码大多都是辅助代码,真正的核心代码委托给了getObjectFromFactoryBean

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
   if (factory.isSingleton() && containsSingleton(beanName)) {
      synchronized (getSingletonMutex()) {
         Object object = this.factoryBeanObjectCache.get(beanName);
         if (object == null) {
               //重点方法 doGetObjectFromFactoryBean
            object = doGetObjectFromFactoryBean(factory, beanName);
            //...省略
   }
   else {
      //重点方法 doGetObjectFromFactoryBean
      Object object = doGetObjectFromFactoryBean(factory, beanName);
      if (shouldPostProcess) {
         try {
            object = postProcessObjectFromFactoryBean(object, beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
         }
      }
      return object;
   }
}

这段代码就是返回的 bean 如果是单例的则需要保证全局唯一,也正因为是单例的所以不必重复创建,可以用缓存来提高性能。


我们进入doGetObjectFromFactoryBean方法中。

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
      throws BeanCreationException {
   Object object;
   try {
      //是否需要权限校验
      if (System.getSecurityManager() != null) {
         AccessControlContext acc = getAccessControlContext();
         try {
            object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
         }
         catch (PrivilegedActionException pae) {
            throw pae.getException();
         }
      }
      else {
         //直接调用getObject方法(重点)
         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) {
      if (isSingletonCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(
               beanName, "FactoryBean which is currently in creation returned null from getObject");
      }
      object = new NullBean();
   }
   return object;
}

在该方法中,我们终于看到了想要看到的代码,也就是object = factory.getObject()。之前我们已经讲过了 FactoryBean 的调用方法,如果 bean 是 FactoryBean 类型,则当提取 bean 时提取的并不是 factoryBean 而是 factoryBean 的 getObject 方法的返回值。


获取单例

之前我们说过如果缓存中不存在已经加载的 bean 则需要重头开始 bean 的加载,在 Spring 中使用 getSingleton 的重载方法实现了 bean 的加载过程。

getBean 方法:
// 实例化依赖的bean后就可以实例化mbd本身了
// 如果BeanDefinition为单例
if (mbd.isSingleton()) {
   //创建Bean实例对象,并且注册给所依赖的对象
   sharedInstance = getSingleton(beanName, () -> {
      try {
         return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
         //从单例缓存中删除bean实例
         //因为单例模式下为了解决循环依赖,可能它已经存在了,所以将其销毁
         destroySingleton(beanName);
         throw ex;
      }
   });
   //如果是普通bean,直接返回,如果是FactoryBean,则返回它的getObject
   bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}

进入 getSingleton 方法:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   //加锁
   synchronized (this.singletonObjects) {
      //首先检查对应的bean是否已经加载过,
      Object singletonObject = this.singletonObjects.get(beanName);
      //如果为空则需要进行singleton的bean初始化
      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 + "'");
         }
         //代码(1)
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         try {
            // 通过回调方式获取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.
            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;
            }
            //代码(2)
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
            //加入缓存 代码(3)
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

上述代码使用了回调方法,在单例创建的前后做了些准备及处理操作,而真正获取单例 bean 的方法是在 ObjectFactory 类型的实例 singletonFactory 中。我们先看一下 getSingleton 方法主要做了什么:


  1. 检查缓存是否已经加载过
  2. 若没有加载,则记录 beanName 为正在加载状态
  3. 加载单例前记录加载状态,代码(1)
protected void beforeSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
   }
}

这个方法主要做的就是记录加载状态,this.singletonsCurrentlyInCreation.add(beanName)将当前正要创建的 bean 记录在缓存中,这样就可以对循环依赖进行检测。


  1. 获取 bean 实例
  2. 调用加载单例后的处理方法,代码(2)
protected void afterSingletonCreation(String beanName) {
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
      throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
   }
}

和上述类似,只是这里是从缓存中移除 bean 的正在加载状态。


  1. 将 bean 加入缓存,并删除加载 bean 过程中所记录的各种辅助状态。
protected void addSingleton(String beanName, Object singletonObject) {
   synchronized (this.singletonObjects) {
      this.singletonObjects.put(beanName, singletonObject);
      this.singletonFactories.remove(beanName);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
   }
}
  1. 返回处理结果

现在我们已经了解了 bean 的逻辑架构,但是 bean 的加载逻辑是在传入 ObjectFactory 类型的参数 singletonFactory 中定义的。

//创建Bean实例对象,并且注册给所依赖的对象
sharedInstance = getSingleton(beanName, () -> {
   try {
      return createBean(beanName, mbd, args);
   }
   catch (BeansException ex) {
      //从单例缓存中删除bean实例
      //因为单例模式下为了解决循环依赖,可能它已经存在了,所以将其销毁
      destroySingleton(beanName);
      throw ex;
   }
});

我们进入 createBean 方法中继续查看。


准备创建 bean

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
   if (logger.isTraceEnabled()) {
      logger.trace("Creating instance of bean '" + beanName + "'");
   }
   RootBeanDefinition mbdToUse = mbd;
   // Make sure bean class is actually resolved at this point, and
   // clone the bean definition in case of a dynamically resolved Class
   // which cannot be stored in the shared merged bean definition.
   // 锁定class,根据设置的class属性或者根据className来解析Class
   Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
   }
   // Prepare method overrides.
   //验证及准备覆盖的方法
   try {
      mbdToUse.prepareMethodOverrides();
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
            beanName, "Validation of method overrides failed", ex);
   }
   try {
      // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
      //给BeanPostProcessors一个机会来返回代理用于替代真正的实例
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      if (bean != null) {
         return bean;
      }
   }
   catch (Throwable ex) {
      throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
            "BeanPostProcessor before instantiation of bean failed", ex);
   }
   try {
      //代码(1)
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isTraceEnabled()) {
         logger.trace("Finished creating instance of bean '" + beanName + "'");
      }
      return beanInstance;
   }
   catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
      // A previously detected exception with proper bean creation context already,
      // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
      throw ex;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(
            mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
   }
}
  1. 根据设置的 class 属性或者根据 className 来解析 Class
  2. 对 override 属性进行标记及验证


我们知道在 Spring 的配置中并没有类似于 override-methdo 之类的配置,那么该方法的作用是什么?

我们之前说过 Spring 配置中存在 lookup-method 和 replace-method 的,这两个配置的加载就是将配置统一存放在 BeanDefinition 中的 methodOverrides 属性中,而这个函数其实也就是针对这两个配置的。


  1. 应用初始化前的后处理器,解析指定 bean 是否存在初始化前的短路操作
  2. 创建 bean


我们先看一下 override 属性标记及验证的逻辑实现


处理 override 属性

进入 prepareMethodOverrides 方法:

public void prepareMethodOverrides() throws BeanDefinitionValidationException {
   // Check that lookup methods exist and determine their overloaded status.
   if (hasMethodOverrides()) {
      getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride);
   }
}
protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
   // 获取对应类中对应方法名的个数
   int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
   if (count == 0) {
      throw new BeanDefinitionValidationException(
            "Invalid method override: no method with name '" + mo.getMethodName() +
            "' on class [" + getBeanClassName() + "]");
   }
   else if (count == 1) {
      // Mark override as not overloaded, to avoid the overhead of arg type checking.
      // 标记MethodOverride暂未被覆盖,避免参数类型检查的开销
      mo.setOverloaded(false);
   }
}

刚才说到 lookup-method 和 replace-method 两个配置功能是统一存放在 BeanDefinition 中的 methodOverrides 属性中,这两个功能实现原理就是在 bean 实例化的时候如果检测到存在 methodOverrides 属性,就会动态地为当前 bean 生成代理并使用对应的拦截器对 bean 做增强处理。


需要提到的是,对于方法的匹配来说,如果一个类中有多个重载方法则需要根据参数类型进行匹配。如果类中只有方法只有一个那么就设置该方法没有被重载,这样在后续时候可以直接使用找到的方法,不需要进行方法的参数匹配验证,而且还可以提前对方法存在性进行验证,所谓一箭双雕。


实例化的前置处理

在调用 doCreateBean 之前,还使用了 resolveBeforeInstantiation(beanName, mbdToUse)方法对 BeanDefinition 中的属性做些前置处理。

Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
   return bean;
}

该段代码的重点就是这个 if 条件,当处理结果不为 null 的时候就会跳过后续 bean 的创建直接返回结果。我们熟知的 AOP 功能就是基于这里判断的。

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        //如果还没被解析过,则解析
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            // Make sure bean class is actually resolved at this point.
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                Class<?> targetType = determineTargetType(beanName, mbd);
                if (targetType != null) {
                    //代码(1)
                    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                    if (bean != null) {
                        //代码(2)
                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                    }
                }
            }
            mbd.beforeInstantiationResolved = (bean != null);
        }
        return bean;
}

该方法中的重点就是这两个方法:applyBeanPostProcessorsBeforeInstantiation、applyBeanPostProcessorsAfterInitialization。这两个方法实现都很简单,就是对后处理器中所有的 InstantiationAwareBeanPostProcessor 类型的后处理器进行 postProcessBeforeInstantiation 方法和 BeanPostProcessor 的 postProcessAfterInitialization 方法的调用。


1. 实例化前的后处理器应用

bean 的实例化前调用,也就是将 AbstractBeanDefinition 转换为 BeanWrapper 前的处理。给子类一个修改 BeanDefinition 的机会,也就是调用这个方法后 bean 就有可能产生变化,有可能是经过处理的代理 bean,也可能是 cglib 生成的。后续会详细介绍,现在只需要明白在 bean 实例化前会调用后处理器的方法进行处理。

protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
   for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof InstantiationAwareBeanPostProcessor) {
         InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
         Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
         if (result != null) {
            return result;
         }
      }
   }
   return null;
}

2. 实例化后的后处理器应用

Spring 的规则是在 bean 初始化后尽可能保证将注册的后处理器 postProcessAfterInitialization 方法应用到 bean 中,如果返回的 bean 不为空,则不需要再经历 bean 的创建过程。

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
      throws BeansException {
   Object result = existingBean;
   for (BeanPostProcessor processor : getBeanPostProcessors()) {
      Object current = processor.postProcessAfterInitialization(result, beanName);
      if (current == null) {
         return result;
      }
      result = current;
   }
   return result;
}


相关文章
|
18天前
|
Java 应用服务中间件 Nacos
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
31 0
|
21天前
|
监控 数据可视化 安全
一套成熟的Spring Cloud智慧工地平台源码,自主版权,开箱即用
这是一套基于Spring Cloud的智慧工地管理平台源码,具备自主版权,易于使用。平台运用现代技术如物联网、大数据等改进工地管理,服务包括建设各方,提供人员、车辆、视频监控等七大维度的管理。特色在于可视化管理、智能报警、移动办公和分布计算存储。功能涵盖劳务实名制管理、智能考勤、视频监控AI识别、危大工程监控、环境监测、材料管理和进度管理等,实现工地安全、高效的智慧化管理。
|
3天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
13 1
|
4天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
46 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
11天前
|
消息中间件 安全 Java
在Spring Bean中,如何通过Java配置类定义Bean?
【4月更文挑战第30天】在Spring Bean中,如何通过Java配置类定义Bean?
20 1
|
12天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
14天前
|
前端开发 Java 数据格式
【Spring系列笔记】定义Bean的方式
在Spring Boot应用程序中,定义Bean是非常常见的操作,它是构建应用程序的基础。Spring Boot提供了多种方式来定义Bean,每种方式都有其适用的场景和优势。
32 2
|
16天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
22天前
|
Java Maven Nacos
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
31 0
|
23天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
34 0
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例