揭开spring初始化方法的神秘面纱

时间:2022-07-26
本文章向大家介绍揭开spring初始化方法的神秘面纱,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

接着上一篇文章《如果你不知道spring中的这些初始化方法,你就out了》,spring有三种初始化方式:

1.设置init-method方法

2.现实InitializingBean接口重写afterPropertiesSet方法

3.在方法上使用PostConstruct注解

这三种初始化方法的执行顺序是:PostConstruct > InitializingBean > init-method

那么问题来了,为什么是这样执行顺序?

我们一起看一下spring的源码

先看一下AbstractAutowireCapableBeanFactory类的doCreateBean方法,我们猜想它是处理PostConstruct注解的,不信我们一起看看。

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;
}

图1

然后看一下applyBeanPostProcessorsAfterInitialization方法,

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;
}

图2

查看postProcessBeforeInitialization时发现了很多类都重写了该方法,到底要进入哪个类的方法呢?

图3

我们重点看InitDestroyAnnotationBeanPostProcessor类的postProcessBeforeInitialization方法,它先查找或创建LifecycleMetadata。

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
   LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
   try {
      metadata.invokeInitMethods(bean, beanName);
   }
   catch (InvocationTargetException ex) {
      throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
   }
   return bean;
}

图4

再看看findLifecycleMetadata方法,先从缓存中查一下有没有LifecycleMetadata对象,如果有就直接返回;如果没有就创建,然后放到缓存中。当然这里用了使用了双重检测锁防止并发的情况判断有问题。

private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
   if (this.lifecycleMetadataCache == null) {
      // Happens after deserialization, during destruction...
      return buildLifecycleMetadata(clazz);
   }
   // Quick check on the concurrent map first, with minimal locking.
   LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
   if (metadata == null) {
      synchronized (this.lifecycleMetadataCache) {
         metadata = this.lifecycleMetadataCache.get(clazz);
         if (metadata == null) {
            metadata = buildLifecycleMetadata(clazz);
            this.lifecycleMetadataCache.put(clazz, metadata);
         }
         return metadata;
      }
   }
   return metadata;
}

图5

接下来重点看一下buildLifecycleMetadata方法

private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
   final boolean debug = logger.isDebugEnabled();
   List<LifecycleElement> initMethods = new ArrayList<>();
   List<LifecycleElement> destroyMethods = new ArrayList<>();
   Class<?> targetClass = clazz;
   do {
      final List<LifecycleElement> currInitMethods = new ArrayList<>();
      final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
      ReflectionUtils.doWithLocalMethods(targetClass, method -> {
         if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
            LifecycleElement element = new LifecycleElement(method);
            currInitMethods.add(element);
            if (debug) {
               logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
            }
         }
         if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
            currDestroyMethods.add(new LifecycleElement(method));
            if (debug) {
               logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);
            }
         }
      });
      initMethods.addAll(0, currInitMethods);
      destroyMethods.addAll(currDestroyMethods);
      targetClass = targetClass.getSuperclass();
   }
   while (targetClass != null && targetClass != Object.class);
   return new LifecycleMetadata(clazz, initMethods, destroyMethods);
}

图6

注意这里关键代码

if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
   LifecycleElement element = new LifecycleElement(method);
   currInitMethods.add(element);
   if (debug) {
      logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
   }
}

图7

我们可以看到程序这里判断如果有initAnnotationType类型的注解,我们就创建LifecycleElement对象,放到一个currInitMethods集合中,然后将currInitMethods集合放到 initMethods集合中,最后创建一个包含了initMethods集合的LifecycleMetadata对象返回。

看到这里不禁想问initAnnotationType是什么鬼?

图8

图9

这里可以看到initAnnotationType是InitDestroyAnnotationBeanPostProcessor类的一个成员变量,通过调用setInitAnnotationType方法赋值的。那么setInitAnnotationType是在哪来调用的?

可以追溯到CommonAnnotationBeanPostProcessor的构造函数,

图10

惊喜,原来initAnnotationType就是PostConstruct。

以上代码的意思,无非是通过反射查询方法上面如果有PostConstruct就将该方法收集起来放到LifecycleMetadata对象中。

接下来,我们再回到图4的地方,会执行这一行代码metadata.invokeInitMethods(bean, beanName);

public void invokeInitMethods(Object target, String beanName) throws Throwable {
   Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods;
   Collection<LifecycleElement> initMethodsToIterate =
         (checkedInitMethods != null ? checkedInitMethods : this.initMethods);
   if (!initMethodsToIterate.isEmpty()) {
      boolean debug = logger.isDebugEnabled();
      for (LifecycleElement element : initMethodsToIterate) {
         if (debug) {
            logger.debug("Invoking init method on bean '" + beanName + "': " + element.getMethod());
         }
         element.invoke(target);
      }
   }
}

图11

会循环收集到的打了PostConstruct注解的方法,

public void invoke(Object target) throws Throwable {
   ReflectionUtils.makeAccessible(this.method);
   this.method.invoke(target, (Object[]) null);
}

图12

直接通过反射调用该方法,即可完成初始化的功能。

至此,PostConstruct注解已经讲解完毕。

接下来,我们回头看一下图1 中的invokeInitMethods(beanName, wrappedBean, mbd);方法,

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.isDebugEnabled()) {
         logger.debug("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);
      }
   }
}

图13

这段代码的意思是,先判断如果当前bean实现了InitializingBean接口,就调用afterPropertiesSet方法。

哈哈哈,这不就是我们要找的InitializingBean接口的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);
      }
   }

图14

获取initMethod方法名称,判断方法名称如果不为空,并且没有继承InitializingBean接口并且InitializingBean不是“afterPropertiesSet”,则调用invokeCustomInitMethod方法,

protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)
      throws Throwable {

   String initMethodName = mbd.getInitMethodName();
   Assert.state(initMethodName != null, "No init method set");
   final Method initMethod = (mbd.isNonPublicAccessAllowed() ?
         BeanUtils.findMethod(bean.getClass(), initMethodName) :
         ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));
   if (initMethod == null) {
      if (mbd.isEnforceInitMethod()) {
         throw new BeanDefinitionValidationException("Could not find an init method named '" +
               initMethodName + "' on bean with name '" + beanName + "'");
      }
      else {
         if (logger.isDebugEnabled()) {
            logger.debug("No default init method named '" + initMethodName +
                  "' found on bean with name '" + beanName + "'");
         }
         // Ignore non-existent default lifecycle methods.
         return;
      }
   }

   if (logger.isDebugEnabled()) {
      logger.debug("Invoking init method  '" + initMethodName + "' on bean with name '" + beanName + "'");
   }

   if (System.getSecurityManager() != null) {
      AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
         ReflectionUtils.makeAccessible(initMethod);
         return null;
      });
      try {
         AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
            initMethod.invoke(bean), getAccessControlContext());
      }
      catch (PrivilegedActionException pae) {
         InvocationTargetException ex = (InvocationTargetException) pae.getException();
         throw ex.getTargetException();
      }
   }
   else {
      try {
         ReflectionUtils.makeAccessible(initMethod);
         initMethod.invoke(bean);
      }
      catch (InvocationTargetException ex) {
         throw ex.getTargetException();
      }
   }
}

图15

通过initMethod方法名称反射获取method对象,如果method对象为空,则直接返回,如果不为空则调用initMethod.invoke(bean);

good,这样就可以调用到init-method指定的具体方法了。

好了,好了,废话不多说了,

通过源代码可以清楚的看到spring初始化方法的调用过程,在applyBeanPostProcessorsBeforeInitialization方法中先调用 打了PostConstruct注解的方法,然后 在invokeInitMethods方法中,先调用 实现了InitializingBean接口的afterPropertiesSet方法,紧接着调用 initMethod对应的具体方法。

所以初始化顺序是:

PostConstruct > InitializingBean > init-method

如果你是一个好奇的孩纸,可能会问:CommonAnnotationBeanPostProcessor对象是在哪来初始化的?谜底将会在下一篇文章中揭晓。