Spring中Bean的初始化

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

  上周因为出去跑了一个 30km 导致文章没有写,今天在写的时候,尽然感觉有点小难受???坚持了好久的事情,不能这么轻易的放弃。有些事情我真的很怕到最后给自己的答案是:这个事情本来我可以,但是...(写在前面给自己打打气)

  上一篇文章,因为要做一个分享所以简单的总结了一下 Spring IoC相关的知识点。今天这篇文章还是接着之前没写完的 IoC 的部分继续往下写。在上上篇文章 Spring 对象注入循环依赖框架是怎么帮我们做的? 分析了 IOC 总循环依赖的处理(掘金的图片显示不出来,这里是本人微信公众号里面的文章)。在Spring 注入对象处理过程中详细分析了对象的注入,其中涉及到一个非常重要的方法populateBean() 在该方法中完成了 Bean 依赖关系的处理。今天分析在对象之间的依赖关系处理完成之后,Spring是如何初始化 Bean 的。

Bean的初始化

  Bean 的初始化方法入口如下:Spring中Bean的初始化  首先简单的总结一下这个方法的处理过程,分为如下几个步骤:

  • Aware 系列接口的回调
  • 执行 BeanPostProcessorsapplyBeanPostProcessorsBeforeInitialization 方法
  • 激活用户自定义的 init 方法
  • 执行 BeanPostProcessorsapplyBeanPostProcessorsAfterInitialization 方法

  方法实现如下

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 {
   /**
    * 对特殊的bean处理:Aware,BeanClassLoaderAware,BeanFactoryAware
    */

   invokeAwareMethods(beanName, bean);
  }

  Object wrappedBean = bean;
  if (mbd == null || !mbd.isSynthetic()) {
   /** 执行BeanPostProcessors的before 方法*/
   wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  }

  try {
   /**
    * 激活用户自定义的 init 方法
    */

   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()) {
   /** 执行BeanPostProcessors的after 方法*/
   wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  }

  return wrappedBean;
 }

1. Aware 系列接口的回调

  在上述代码中,Aware 接口的回调是通过 invokeAwareMethods(beanName, bean); 来完成的。在这个方法中,对特殊的bean处理:AwareBeanClassLoaderAwareBeanFactoryAware等。

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

Aware 系列的接口

Spring中Bean的初始化
Aware系列部分接口

「常用的Aware相关接口作用说明」

名称 作用
ApplicationContextAware 获取spring 上下文环境的对象
BeanNameAware 获取该bean在BeanFactory配置中的名字
BeanFactoryAware 创建它的BeanFactory实例

  以 ApplicationContextAware 为例简单做一个使用说明,在 Spring 官网中主要介绍了这个类,官方文档还是要多看看的...测试代码如下:

「启动类:」

public class MyTestStart {
 public static void main(String[] args) {
  // Aware 使用测试
  ApplicationContext ann = new AnnotationConfigApplicationContext(MyConfig.class);
  MyAwareTestService service = (MyAwareTestService) ann.getBean("myAwareTestService");
  service.myTest();
  service.myTest();
 }
}

「单例的Bean:」

@Service("myAwareTestService")
public class MyAwareTestService {
 @Autowired
 Test test;
 @Autowired
 TestAware1 testAware1;

 public void myTest(){
  testAware1.test();
  test.test();
 }
}

「原型 Bean:」

@Service
@Scope("prototype")
public class Test {

 public void test(){
  System.out.println("Test ->" + this);
 }
}

Spring中Bean的初始化注意,知识点来了:如果一个单例的 Bean 依赖了一个原型的 Bean,那么依赖的这个 Bean 是单例的还是原型的?如果是单例的,那么怎么才能拿到原型的?

答案:其实 Spring 在准备环境的时候,会实例化所有的(除去懒加载) Bean,然后缓存起来(还记得 Spring 当中的三个 Map 吗?),在 getBean() 的时候如果发现缓存中已经有了,就会直接拿到不会创建了。因此,如果不做特殊处理的话,单例 Bean 中依赖原型的 Bean 拿到的也是同一个 Bean。那么问题来了,这个有解决方法吗?自然是有的,但是为了不偏离本文的测重点,这里只给出方案,不做原理上的深究。其实解决这个问题,可以通过以下三种途径来解决:

  • 通过上述的 ApplicationContextAware。实现该接口,拿到 ApplicationContext,然后通过 getBean() 获取对象,然后在使用。
  • 在原型 Bean 中注入 ApplicationContext,然后通过 getBean() 获取对象,然后在使用。
  @Service
  @Scope("prototype")
  public class Test {

 @Autowired
 ApplicationContext applicationContext;

 public void test(){
  System.out.println("Test ->" + applicationContext.getBean("test"));
 }
  }
  • 使用@Lookup注解。
  Service
  @Scope("prototype")
  public abstract class Test1 {
   @Lookup
   protected abstract Test1 methodInject();

   public void test(){
      Test1 test1 = methodInject();
   System.out.println("Test ->" + test1);
   }
  }

运行结果:Spring中Bean的初始化

2. applyBeanPostProcessorsBeforeInitialization 方法的执行

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

  容器初始化之前做最后的检查,检查 BeanPostProcessorBean做相应的处理。

3. 激活用户自定义的 init 方法

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
   throws Throwable {

  /** 首先检查是否是 InitializingBean,如果是的话需要调用afterPropertiesSet方法*/
  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 {
    // 属性 初始化后的处理,调用 afterPropertiesSet()方法
    ((InitializingBean) bean).afterPropertiesSet();
   }
  }
  /**
   * 从代码看出:
   *  afterPropertiesSet() 和 init-method() 都是在初始化bean时执行,
   *  执行顺序是 先 afterPropertiesSet()方法执行,
   *           然后 init-method() 方法执行
   */
  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);
   }
  }
 }

  这里是处理实现了 InitializingBean 接口,被重写了的 afterPropertiesSet() 方法;以及 init-method() 方法的处理。通过下面的 invokeCustomInitMethod() 方法对  @Bean 中的 initMethod 属性或 xmlinit-Method 进行相应的处理。

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

    /**
     * 获取 Bean 定义的 @Bean 中的 initMethod 属性 或xml中init-Method
     */
    String initMethodName = mbd.getInitMethodName();
    Assert.state(initMethodName != null, "No init method set");
    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.isTraceEnabled()) {
                logger.trace("No default init method named '" + initMethodName +
                        "' found on bean with name '" + beanName + "'");
            }
            // Ignore non-existent default lifecycle methods.
            return;
        }
    }

    if (logger.isTraceEnabled()) {
        logger.trace("Invoking init method  '" + initMethodName + "' on bean with name '" + beanName + "'");
    }
    Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);

    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            ReflectionUtils.makeAccessible(methodToInvoke);
            return null;
        });
        try {
            AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
                    methodToInvoke.invoke(bean), getAccessControlContext());
        }
        catch (PrivilegedActionException pae) {
            InvocationTargetException ex = (InvocationTargetException) pae.getException();
            throw ex.getTargetException();
        }
    }
    else {
        try {
            ReflectionUtils.makeAccessible(methodToInvoke);
            /**
             * 通过 JDk 的反射机制得到 Method 对象
             * 直接调用在 Bean 中定义声明的初始化方法
             */
            methodToInvoke.invoke(bean);
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }

4. applyBeanPostProcessorsAfterInitialization

  这里就是对 BeanPostProcessor 中的另一个方法的处理过程。之前的文章多次提到过,这里就不在赘述了。

5. 总结

  这篇文章中分析了依赖关系处理完成之后,Bean的初始化的过程,通过上述的4个方面做了简单的分析。从中也了解了Spring中设计的 Aware 接口,通过一道面试题分析简单的使用了一下 ApplicationContextAware

  其实到这里,doCreateBean() 方法已经分析完了,在这个方法中主要完成了一下几件事情 ①:如果是单例首先需要清除缓存;②:实例化 bean,将 BeanDefinition 转化为 BeanWrapper ;③:MergedBeanDefinitionPostProcessor 的应用;④:依赖处理;⑤:属性填充;⑥:循环依赖处理;⑦:注册 DisposableBean;⑧:完成创建并返回。也就是说 Bean 的创建完成了。

  其实到这里,doCreateBean() 方法完成之后,doGetBean() 方法中的流程也已经基本完成了,最后剩了一个 「类型转换」,程序运行到这里bean的初始化基本结束,requiredType 通常是为空的,但是可能存在这样的情况,返回的 Bean 是个 String,但是 requiredTypeInteger ,这个时候这个步骤就有作用了:将返回的 Bean 转换为 requiredType 所指定的类型。

  关于 doGetBean() 我会在后面的文章中,做一个详细的总结。


原文始发于微信公众号(Fchen):Spring中Bean的初始化