Spring 源码阅读 27:Bean 实例初始化

 2023-01-09
原文作者:Pseudocode 原文地址:https://juejin.cn/post/7137703037258170398

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 26:Bean 早期实例处理及属性注入

前情提要

上一篇分析了 Spring 完成早期 Bean 实例的创建后的一部分流程,其中包括很重要的属性注入的部分,这一篇接着分析之后的代码。在doCreateBean方法中,属性注入的部分在populateBean方法中完成,本文分析initializeBean方法的源码,它在populateBean执行完成之后执行,完成 Bean 实例的初始化。

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
       populateBean(beanName, mbd, instanceWrapper);
       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);
       }
    }

Bean 实例的初始化

进入initializeBean方法查看源码。

    // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)
    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
       if (System.getSecurityManager() != null) {
          AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
             invokeAwareMethods(beanName, bean);
             return null;
          }, getAccessControlContext());
       }
       else {
          invokeAwareMethods(beanName, bean);
       }
    
       Object wrappedBean = bean;
       if (mbd == null || !mbd.isSynthetic()) {
          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()) {
          wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
       }
    
       return wrappedBean;
    }

虽然也是一个很重要的步骤,但是其中的代码并不多,而且仔细分析之后,发现关键代码就是以下的 4 行,从调用的方法名称,也能知道它们的具体作用:

  1. invokeAwareMethods(beanName, bean)执行感知接口的方法。
  2. applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)执行后处理器中初始化之前的处理
  3. invokeInitMethods(beanName, wrappedBean, mbd)执行初始化方法
  4. applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)执行后处理器中初始化之后的处理

接下来逐一分析。

执行感知接口的方法

先看invokeAwareMethods方法。

    // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods
    private void invokeAwareMethods(final String beanName, final Object bean) {
       if (bean instanceof Aware) {
          if (bean instanceof BeanNameAware) {
             ((BeanNameAware) bean).setBeanName(beanName);
          }
          if (bean instanceof BeanClassLoaderAware) {
             ClassLoader bcl = getBeanClassLoader();
             if (bcl != null) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
             }
          }
          if (bean instanceof BeanFactoryAware) {
             ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
          }
       }
    }

这里的逻辑很简单,如果 Bean 实现了 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 三个接口中的一个或多个,就调用接口的方法,将对应的属性注入到 Bean 实例中。这里只处理了这三个跟 Bean 本身有关的感知接口,其他的比如 ApplicationContextAware 等感知接口的处理逻辑,其实在容器初始化的过程中,已经通过 BeanPostProcessor 的方式注册到了容器中,可以参考 Spring 源码阅读 12:BeanFactory 预处理 中的「添加感知接口的后处理器」小节。

执行后处理器在初始化前后的处理逻辑

接下来看后处理器的处理逻辑,分别在执行 Bean 初始化方法的前后执行了 BeanPostProcessor 的postProcessBeforeInitializationpostProcessAfterInitialization方法。

    // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization
    @Override
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
          throws BeansException {
    
       Object result = existingBean;
       for (BeanPostProcessor processor : getBeanPostProcessors()) {
          Object current = processor.postProcessBeforeInitialization(result, beanName);
          if (current == null) {
             return result;
          }
          result = current;
       }
       return result;
    }
    // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
    @Override
    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;
    }

这两个方法的逻辑除了调用了后处理器的不同方法以外,其他都没什么区别。

在分析 Spring 容器初始化的代码时,曾经详细介绍过 BeanPostProcessor,如果我们需要自定义一些 Bean 初始化前后的处理逻辑,可以开发一个 BeanPostProcessor 的实现类,并将其作为 Bean 添加到配置文件中,在 Spring 初始化容器的过程中,会将所有 BeanPostProcessor 类型的 Bean 都作为后处理器注册到容器中,并且在注册前还进行了排序。因此,在这里执行后处理器逻辑的时候,直接按列表中的顺序执行就可以了。

关于 BeanPostProcessor 的更详细内容,以及 BeanPostProcessor 是如何注册到容器中的,可以参考这篇:Spring 源码阅读 14:注册 BeanPostProcessor

执行 Bean 的初始化方法

接下来看invokeInitMethods方法。

    // org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods
    protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
          throws Throwable {
    
       boolean isInitializingBean = (bean instanceof InitializingBean);
       if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
          if (logger.isTraceEnabled()) {
             logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
          }
          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();
          if (StringUtils.hasLength(initMethodName) &&
                !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
             invokeCustomInitMethod(beanName, bean, mbd);
          }
       }
    }

这段代码的逻辑也很简单,主要是两部分:

  1. 如果当前处理的 Bean 实例实现了 InitializingBean 接口,那么调用它的afterPropertiesSet方法。
  2. 从 BeanDefinition 中获取initMethodName,也就是初始化方法名称,在 Bean 实例上执行这个方法。

这两部分分开来讲。

InitializingBean

先看 InitializingBean 接口是什么。

    public interface InitializingBean {
    
        void afterPropertiesSet() throws Exception;
    
    }

接口中只定义了一个afterPropertiesSet方法,也就是说,如果一个 Bean 实现了 InitializingBean 接口,那么它的afterPropertiesSet将会在这个时候被调用。因此,如果我们需要一个 Bean 在初始化的时候执行一段特定的逻辑,可以通过实现 InitializingBean 接口,在afterPropertiesSet实现这些逻辑。

这一部分只针对实现了 InitializingBean 接口的 Bean 实例。

自定义初始化方法

接下来看第二部分。首先会获取 BeanDefinition 的initMethodName成员变量,它其实对应的 XML 配置文件中bean标签的init-method属性,这个属性的值对应类中的一个同名的无参方法。配置方法如下:

    <bean id="user" class="xxx.User" init-method="init" />

获取到方法名,在执行之前,还要进行以下一系列判断:

  • 方法名不能是空的
  • 如果当前的 Bean 实现了 InitializingBean 接口,且这里配置的方法名是afterPropertiesSet,那么,这个方法在之前已经执行过了,这里不在执行。
  • 通过isExternallyManagedInitMethod方法判断,如果是外部管理的初始化方法,则不在此处执行。

判断无误后,会通过invokeCustomInitMethod执行这个自定义的初始化方法。这个方法的代码比较多,但是逻辑比较简单,就是通过反射,执行方法。

总结

至此,initializeBean方法中的源码就分析完了。完成了初始化的方法之后,Spring 创建和初始化 Bean 实例的流程也接近了尾声,后面还有一些最后的处理工作,留到下一篇中进行分析。