美文网首页
3-bean 实例化——3-5 初始化

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

作者: 鹏程1995 | 来源:发表于2020-02-25 16:00 被阅读0次

    概要

    过度

    我们上文介绍了创建 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()出现了好几次了,干啥的?

    参考文献

    相关文章

      网友评论

          本文标题:3-bean 实例化——3-5 初始化

          本文链接:https://www.haomeiwen.com/subject/nfwtchtx.html