likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 22

基于 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 实例的流程也接近了尾声,后面还有一些最后的处理工作,留到下一篇中进行分析。