3-bean 实例化——3-5 初始化

2020-02-25  本文已影响0人  鹏程1995

概要

过度

我们上文介绍了创建 Bean 实例并进行依赖注入、初始化的整体逻辑,我们介绍的是doCreateBean方法。还画了一个流程图,如下图所示:

1.png

其中,我们分别在3-3,3-4中介绍了创建Bean实例、依赖注入的操作。现在我们拿到了 Bean 的一个实例引用,而且完成了对 Bean 实例中的所有需要的属性的依赖注入。我们之前说过在得到 Bean 实例后有两条路:

  1. 看如果是单例的话就提前暴露出去方便解决循环依赖,在最后完成创建后看是否成功解决了循环依赖
  2. 继续进行 Bean 的创建,包括依赖注入和调用初始化函数

其中,第一条路我们放在最后,在收束整个创建 Bean 实例流程时进行讲解。本节从第二条路入手,继续讲解 Bean 实例创建的整个流程。

我们本节介绍调用初始化钩子,对 Bean 实例进行最后的包装。

内容简介

本节介绍对得到的 Bean 实例进行初始化钩子调用的步骤。

所属环节

调用初始化相关钩子。

上下环节

上文: 创建 Bean 实例【根据配置和入参获得创建实例的函数并获得实例。然后进行依赖注入】

下午: 获得实例,进行循环依赖是否成功解决的判断。

源码解析

入口

我们看一下doCreateBean中的相关调用

try {
  // 填充一些 bean 的属性
  populateBean(beanName, mbd, instanceWrapper);
  // 初始化 bean
  // 这里才叫初始化,初始化特指调用初始化和那些钩子方法,其他的包括填充属性,都叫创建实例
  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);
  }
}

其实就一行:

exposedObject = initializeBean(beanName, exposedObject, mbd);

我们接下来看看里面的逻辑:

初始化钩子调用的逻辑

// 初始化 bean ,主要调用 bean 的初始化方法【如果这个 bean 实现了对应的接口】以及工厂的一些回调函数
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
  // 根据 bean 实现的 Aware 接口【aware ---> 知道的,也即是说这个 bean 想要获得的一些信息】,将对应的信息填充进 bean 实例

  // 其实就是约定了一些接口,你实现一下,后面 Spring 会专门把一些你想知道的东西给你扔进去,比如 id、类加载器、创建你这个Bean的工厂啥的
  // TODO 专门介绍一下下
  if (System.getSecurityManager() != null) {
    AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
      invokeAwareMethods(beanName, bean);
      return null;
    }, getAccessControlContext());
  } else {
    invokeAwareMethods(beanName, bean);
  }

  Object wrappedBean = bean;
  // bean 是系统生成的,不是合成的,就调用工厂注册的那些在初始化之前要调用的钩子
  // TODO mbd.isSynthetic() 这个一直有些懵逼
  if (mbd == null || !mbd.isSynthetic()) { // 调用 Factory 中的那些个钩子
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  }

  // 调用初始化方法
  try {
    invokeInitMethods(beanName, wrappedBean, mbd);
  } catch (Throwable ex) {
    throw new BeanCreationException(
      (mbd != null ? mbd.getResourceDescription() : null),
      beanName, "Invocation of init method failed", ex);
  }
  if (mbd == null || !mbd.isSynthetic()) {// 调用 Factory 中的那些个钩子
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  }

  return wrappedBean;
}

思路明确:

  1. 把你想知道的关于上下文的信息给你
  2. 调用注册在 Factory中的钩子
  3. 调用你配置的那些初始化钩子
  4. 调用注册在Factory中的钩子

都调用了哪些我们配置的初始化钩子

// 调用 bean 的初始化方法,初始化方法的指定有两种:
// 1. 用 init-method 指定【目前只关注了 xml 的配置,注解的那个我们后面再分析一遍】
// 2. bean 实现 InitializingBean 接口,直接调用接口下面的初始化方法
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
  throws Throwable {

  boolean isInitializingBean = (bean instanceof InitializingBean);
  // mbs == null 用 或的短路去理解
  // TODO : mbd.isExternallyManagedInitMethod 这个的设置和对应的使用后面可以瞅一下
  if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
    if (logger.isDebugEnabled()) {
      logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
    }

    // 调用 afterPropertiesSet

    if (System.getSecurityManager() != null) {
      try {
        AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
          ((InitializingBean) bean).afterPropertiesSet();
          return null;
        }, getAccessControlContext());
      } catch (PrivilegedActionException pae) {
        throw pae.getException();
      }
    } else {
      ((InitializingBean) bean).afterPropertiesSet();
    }
  }

  if (mbd != null && bean.getClass() != NullBean.class) {
    String initMethodName = mbd.getInitMethodName();
    // 1. 有配置对应的 initMethod,就继续调用
    if (StringUtils.hasLength(initMethodName) &&
        !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
        !mbd.isExternallyManagedInitMethod(initMethodName)) {
      invokeCustomInitMethod(beanName, bean, mbd);
    }
  }
}
  1. 如果实现了InitializingBean接口,就先调用它定义的初始化钩子
  2. 看你还有没有配置哪个函数要初始化的,调用一下

总结

思路整体来说极其顺畅。一杆子到底,没得说。

扩展

问题遗留

Aware系列接口统一介绍

后处理器接口统一介绍

mbd的一些属性

mbd.isExternallyManagedInitMethod干啥的?

mbd.isSynthetic()出现了好几次了,干啥的?

参考文献

上一篇下一篇

猜你喜欢

热点阅读