Spring源码(十二)-初始化Bean-initializeBean

1,368 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第 15 天,点击查看活动详情

日积月累,水滴石穿 😄

前言

在第一篇博文Spring源码(一)-Bean的定义-BeanDefinition中,其中有个属性为initMethodName

也就是对应 bean标签中的 init-method 的属性。那这个属性对象的方法在什么时候被执行呢?我们首先理理这个方法的执行顺序,Spring 已经执行过 bean 的实例化,并且进行了属性的填充,在这些步骤之后将会调用用户设定的初始化方法。

源码

该方法位于AbstractAutowireCapableBeanFactory类中。

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 {
    // 1、执行Aware
    invokeAwareMethods(beanName, bean);
  }
  Object wrappedBean = bean;
  if(mbd == null || !mbd.isSynthetic()) {
    // 2、初始化前
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  }
  try {
    // 3、初始化
    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()) {
    // 4、初始化后
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  }
  return wrappedBean;
}

initializeBean方法里的代码分支很好理解:

  1. 激活 Aware 方法:invokeAwareMethods
  2. 初始化前后置处理器的应用:applyBeanPostProcessorsBeforeInitialization
  3. 激活 init 方法:invokeInitMethods
  4. 初始化后后置处理器的应用:applyBeanPostProcessorsAfterInitialization

1、激活 Aware 方法

在分析其源码之前,我们先了解一下 Aware 的使用。可以看我另外一篇文章:Spring源码-Aware接口

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 实例是否属于 Aware 接口,如果是的话,然后再判断 bean 实例是否属于 xxxAware 接口,如果是,则调用实例的 setXxx() 方法给实例设置 xxx 属性值,在 invokeAwareMethods() 方法里主要是给BeanNameAware设置 beanName,给BeanClassLoaderAware设置 ClassLoader、给 BeanFactoryAware设置 BeanFactory

2、初始化前后置处理器的应用

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

理理上述代码。获得所有 BeanPostProcessor,循环执行BeanPostProcessor中的 postProcessBeforeInitialization初始化前方法。如果初始化前方法返回的结果集为 null,则返回 result。如果不为 null,则将当前方法返回的结果暂存起来,继续执行下一个 BeanPostProcessor中的 postProcessBeforeInitialization初始化前方法。如果对于 BeanPostProcessor不太了解,可以先去看看小杰的另外一篇文章:Spring源码-BeanPostProcessor

3、激活 init 方法

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable {
  // 当前 Bean 是否实现了 InitializingBean 接口
  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();
    }
  }
  if(mbd != null && bean.getClass() != NullBean.class) {
    // 获得bean标签的 init-method 属性
    String initMethodName = mbd.getInitMethodName();
    //1、 方法名不为空
    //2、 是否实现了InitializingBean接口 并且 init-method 属性的值等于 afterPropertiesSet(取反)
    //3、 externallyManagedInitMethods Set集合中是否包含 init-method 属性的值(取反)
    // externallyManagedInitMethods Set集合只记录 afterPropertiesSet
    if(StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(
        initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) {
      // 反射机制执行 init-method="xxx"
      invokeCustomInitMethod(beanName, bean, mbd);
    }
  }
}

首先检测当前 bean 是否实现了 InitializingBean 接口,如果实现了则调用其 afterPropertiesSet()方法,然后再检查当前 bean是否配置了 init-method 属性,如果配置了则通过反射机制调用指定的 init-method()方法。

InitializingBean

SpringInitializingBean 接口为 bean 提供了初始化方法的方式,它仅包含了一个方法:afterPropertiesSet(),凡是实现该接口的类,在初始化bean的时候都会执行该方法。定义如下:

public interface InitializingBean {

	void afterPropertiesSet() throws Exception;

}

提供A类并实现 InitializingBean 接口,重写其 afterPropertiesSet方法

public class A implements InitializingBean {

   private String name;

   @Override
   public void afterPropertiesSet() throws Exception {
      System.out.println("afterPropertiesSet 初始化方法执行");
      this.name = "gongj";
   }
  
   public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

配置与启动类

<bean class="com.gongj.initializing.A" id="a"/>
    
public static void main(String[] args) {
		ClassPathXmlApplicationContext context =
				new ClassPathXmlApplicationContext("spring-config3.xml");
		A bean = context.getBean(A.class);
		System.out.println(bean.getName());
	}
结果:
afterPropertiesSet 初始化方法执行
gongj

afterPropertiesSet() 中可以改变 bean 的属性值,这相当于 Spring 容器又给我们提供了一种可以改变 bean 实例对象的方法。

init-method

在学习Spirng的时候,肯定学过bean标签的init-method属性,该属性用于在 bean 初始化时执行指定方法,可以用来替代 InitializingBean 接口的作用。

修改A类代码,内容如下:

public class A {

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void initMethod(){
		System.out.println("初始化方法执行");
		this.name = "yuanj";
	}
}

修改配置项并启动

<bean class="com.gongj.initializing.A" id="a" init-method="initMethod"/>

结果:
初始化方法执行
yuanj

完全可以达到和 InitializingBean 一样的效果。

那这里就有个问题,如果我同时实现 InitializingBean接口和配置 init-method,那这两个初始化方法谁先执行呢?其实看了上面的源码,应该有了答案,不过这里还是实践一波。

修改A类代码,并启动

public class A implements InitializingBean{

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void initMethod(){
		System.out.println("初始化方法执行");
		this.name = "yuanj";
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("afterPropertiesSet 初始化方法执行");
		this.name = "gongj";
	}
}

结果:
afterPropertiesSet 初始化方法执行
初始化方法执行
yuanj

先执行实现 InitializingBean接口的方法,再执行配置 init-method的方法。

总结

  • Spring提供了两种初始化方法的方式,一是实现InitializingBean接口,重写afterPropertiesSet方法,二是在配置文件中配置init-method指定初始化方法。两种方式可以同时使用,执行顺序是 afterPropertiesSet先执行,而 init-method 后执行。

  • 实现InitializingBean接口是直接调用afterPropertiesSet方法,比通过反射调用init-method指定的方法效率要高一点,但是init-method方式相对而言比较灵活。但就目前而言,我们的工程几乎应该都放弃这两种方式,而是采用注释的方式,所以可以使用 @PostConstruct 注解进行初始化。

  • 如果调用afterPropertiesSet方法时出错,则不调用init-method指定的方法。

初始化后后置处理器的应用

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        // 执行postProcessAfterInitialization方法,也就是初始化后方法
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

获得所有 BeanPostProcessor,循环执行BeanPostProcessor中的 postProcessAfterInitialization初始化后方法。如果初始化后方法返回的结果集为 null,则返回 result。如果不为 null,则将当前方法返回的结果暂存起来,继续执行下一个 BeanPostProcessor中的 postProcessBeforeInitialization初始化后方法。


  • 如你对本文有疑问或本文有错误之处,欢迎评论留言指出。如觉得本文对你有所帮助,欢迎点赞和关注。