Spring 源码分析:@PostConstruct、InitializingBean、ApplicationContextAware、ApplicationListener 的执行顺序


#Spring#


Spring 版本: 5.2.2.RELEASE

先上结论,顺序是:

  1. 构造函数
  2. 依赖的bean被初始化完成,并注入到了当前bean 中 (循环依赖时,稍微不同)
  3. ApplicationContextAware 接口的 setApplicationContext 方法
  4. @PostConstruct 注解的方法
  5. InitializingBean 接口的 afterPropertiesSet 方法
  6. 等所有 bean 初始化完成后,ApplicationListener<ApplicationEvent> 接口的 onApplicationEvent 方法接收 ContextRefreshedEvent 事件。

测试代码准备

项目结构:

src
└── main
    └── java
        └── demo
            ├── Demo004.java
            ├── bean
            │   ├── Bean01.java
            │   └── Bean02.java
            └── util
                └── Utils.java

Utils.java

package demo.util;

import java.time.LocalTime;

public class Utils {

	public static void log(String format, Object... args) {
		// 当前线程名称@线程id
		String threadName = Thread.currentThread().getName()+"@"+Thread.currentThread().getId();
		// 为了方便打印结果的查看,若线程名长度不足16,则补空格
		while (threadName.length() < 16) {
			threadName = threadName + " ";
		}
		// 当前时间 (时-分-秒)
		LocalTime now = LocalTime.now();
		String realFormat = String.format("[%s][%s] %s\n", threadName,  now, format);
		System.out.printf(realFormat, args);
	}

	public static void sleep(int millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			log("sleep 中断异常: %s", e.getMessage());
		}
	}

}

Demo004.java

package demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "demo.bean")
public class Demo004 {

	public static void main(String[] args) {
		ApplicationContext ctx = new AnnotationConfigApplicationContext(Demo004.class);
	}

}

Bean01.java

package demo.bean;

import demo.util.Utils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class Bean01 implements InitializingBean, ApplicationContextAware, ApplicationListener<ApplicationEvent> {

	public Bean01() {
		Utils.log("%s construct", getClassName());
	}

	@PostConstruct
	public void postConstruct() {
		Utils.log("%s @PostConstruct", getClassName());
	}


	@Override
	public void afterPropertiesSet() throws Exception {
		Utils.log("%s InitializingBean.afterPropertiesSet", getClassName());
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		Utils.log("%s ApplicationContextAware.setApplicationContext: %s", getClassName(), applicationContext);
	}

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		Utils.log("%s ApplicationListener.onApplicationEvent: %s", getClassName(), event);
	}

	private String getClassName() {
		return this.getClass().getSimpleName();
	}
}

Bean02.java

package demo.bean;

import demo.util.Utils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class Bean02 implements InitializingBean, ApplicationContextAware, ApplicationListener<ApplicationEvent> {

	public Bean02() {
		Utils.log("%s construct", getClassName());
	}

	@PostConstruct
	public void postConstruct() {
		Utils.log("%s @PostConstruct", getClassName());
	}


	@Override
	public void afterPropertiesSet() throws Exception {
		Utils.log("%s InitializingBean.afterPropertiesSet", getClassName());
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		Utils.log("%s ApplicationContextAware.setApplicationContext: %s", getClassName(), applicationContext);
	}

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		Utils.log("%s ApplicationListener.onApplicationEvent: %s", getClassName(), event);
	}

	private String getClassName() {
		return this.getClass().getSimpleName();
	}
}

测试过程和结论

测试1

执行 Demo04.java ,输出如下:

[main@1          ][16:07:44.017] Bean01 construct
[main@1          ][16:07:44.028] Bean01 ApplicationContextAware.setApplicationContext: org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 16:07:43 CST 2020
[main@1          ][16:07:44.032] Bean01 @PostConstruct
[main@1          ][16:07:44.032] Bean01 InitializingBean.afterPropertiesSet
[main@1          ][16:07:44.034] Bean02 construct
[main@1          ][16:07:44.035] Bean02 ApplicationContextAware.setApplicationContext: org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 16:07:43 CST 2020
[main@1          ][16:07:44.035] Bean02 @PostConstruct
[main@1          ][16:07:44.036] Bean02 InitializingBean.afterPropertiesSet
[main@1          ][16:07:44.051] Bean01 ApplicationListener.onApplicationEvent: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 16:07:43 CST 2020]
[main@1          ][16:07:44.051] Bean02 ApplicationListener.onApplicationEvent: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 16:07:43 CST 2020]

可以得出结论:

对于一个 Spring bean,内部的执行顺序是:

  1. 构造函数
  2. ApplicationContextAware 接口的 setApplicationContext 方法
  3. @PostConstruct 注解的方法
  4. InitializingBean 接口的 afterPropertiesSet 方法
  5. 等所有 bean 初始化完成后,ApplicationListener<ApplicationEvent> 接口的 onApplicationEvent 方法接收 ContextRefreshedEvent 事件。

测试2

在 Bean01 注入 Bean02,即 :

package demo.bean;

// ....

@Component
public class Bean01 implements InitializingBean, ApplicationContextAware, ApplicationListener<ApplicationEvent> {

	@Autowired
	private Bean02 bean02;

	// ...... 其他代码保持不变
}

执行结果:

[main@1          ][16:28:54.192] Bean01 construct
[main@1          ][16:28:54.215] Bean02 construct
[main@1          ][16:28:54.215] Bean02 ApplicationContextAware.setApplicationContext: org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 16:28:53 CST 2020
[main@1          ][16:28:54.220] Bean02 @PostConstruct
[main@1          ][16:28:54.220] Bean02 InitializingBean.afterPropertiesSet
[main@1          ][16:28:54.224] Bean01 ApplicationContextAware.setApplicationContext: org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 16:28:53 CST 2020
[main@1          ][16:28:54.224] Bean01 @PostConstruct
[main@1          ][16:28:54.224] Bean01 InitializingBean.afterPropertiesSet
[main@1          ][16:28:54.238] Bean02 ApplicationListener.onApplicationEvent: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 16:28:53 CST 2020]
[main@1          ][16:28:54.239] Bean01 ApplicationListener.onApplicationEvent: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 16:28:53 CST 2020]

由此,可以得出更细致的结论:

  1. 构造函数
  2. 依赖的bean被初始化完成,并注入到了当前bean 中
  3. ApplicationContextAware 接口的 setApplicationContext 方法
  4. @PostConstruct 注解的方法
  5. InitializingBean 接口的 afterPropertiesSet 方法
  6. 等所有 bean 初始化完成后,ApplicationListener<ApplicationEvent> 接口的 onApplicationEvent 方法接收 ContextRefreshedEvent 事件。

测试3:如果在 @PostConstruct 等方法中sleep一会,会怎么样?

因为默认情况下,所有的 bean 都是在一个线程中生成的。所以,sleep 会导致 Spring 容器初始化时间变长。 改造 Bean01.java :

// ...
@Component
public class Bean01 implements InitializingBean, ApplicationContextAware, ApplicationListener<ApplicationEvent> {

	public Bean01() {
		Utils.log("%s construct", getClassName());
	}

	@PostConstruct
	public void postConstruct() {
		Utils.sleep(2000);
		Utils.log("%s @PostConstruct", getClassName());

	}


	@Override
	public void afterPropertiesSet() throws Exception {
		Utils.sleep(3000);
		Utils.log("%s InitializingBean.afterPropertiesSet", getClassName());
	}

    // ...

执行结果:

[main@1          ][17:51:16.281] Bean01 construct
[main@1          ][17:51:16.295] Bean01 ApplicationContextAware.setApplicationContext: org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 17:51:16 CST 2020
[main@1          ][17:51:18.303] Bean01 @PostConstruct
[main@1          ][17:51:21.308] Bean01 InitializingBean.afterPropertiesSet
[main@1          ][17:51:21.313] Bean02 construct
[main@1          ][17:51:21.314] Bean02 ApplicationContextAware.setApplicationContext: org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 17:51:16 CST 2020
[main@1          ][17:51:21.315] Bean02 @PostConstruct
[main@1          ][17:51:21.315] Bean02 InitializingBean.afterPropertiesSet
[main@1          ][17:51:21.346] Bean01 ApplicationListener.onApplicationEvent: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 17:51:16 CST 2020]
[main@1          ][17:51:21.346] Bean02 ApplicationListener.onApplicationEvent: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 17:51:16 CST 2020]

观察时间,会看到部分输出之间的时间间隔较大。

测试4: 循环依赖

改造 Bean01.java :

// ...

@Component
public class Bean01 implements InitializingBean, ApplicationContextAware, ApplicationListener<ApplicationEvent> {

	@Autowired
	private Bean02 bean02;

	public Bean01() {
		Utils.log("%s construct", getClassName());
	}

	public String hello() {
		return String.format("%s hello.", getClassName());
	}

	@PostConstruct
	public void postConstruct() {
		Utils.log("%s @PostConstruct. bean01.hello(): %s", getClassName(), bean02.hello());
	}

    // ...
}

改造 Bean02.java :

// ...

@Component
public class Bean02 implements InitializingBean, ApplicationContextAware, ApplicationListener<ApplicationEvent> {

	@Autowired
	private Bean01 bean01;

	public Bean02() {
		Utils.log("%s construct", getClassName());
	}

	public String hello() {
		return String.format("%s hello", getClassName());
	}

	@PostConstruct
	public void postConstruct() {
		Utils.log("%s @PostConstruct. bean01.hello(): %s", getClassName(), bean01.hello());
	}

    // ...
}

执行结果:

[main@1          ][16:53:11.748] Bean01 construct
[main@1          ][16:53:11.770] Bean02 construct
[main@1          ][16:53:11.772] Bean02 ApplicationContextAware.setApplicationContext: org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 16:53:11 CST 2020
[main@1          ][16:53:11.775] Bean02 @PostConstruct. bean01.hello(): Bean01 hello.
[main@1          ][16:53:11.775] Bean02 InitializingBean.afterPropertiesSet
[main@1          ][16:53:11.777] Bean01 ApplicationContextAware.setApplicationContext: org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 16:53:11 CST 2020
[main@1          ][16:53:11.777] Bean01 @PostConstruct. bean01.hello(): Bean02 hello
[main@1          ][16:53:11.777] Bean01 InitializingBean.afterPropertiesSet
[main@1          ][16:53:11.795] Bean02 ApplicationListener.onApplicationEvent: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 16:53:11 CST 2020]
[main@1          ][16:53:11.796] Bean01 ApplicationListener.onApplicationEvent: org.springframework.context.event.ContextRefreshedEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 16:53:11 CST 2020]

在 Bean02 的 postConstruct 方法中是能调用 Bean01 的 hello 方法的。可见 Spring 是支持循环依赖的。

Spring 支持的是有限的循环依赖。这是因为,Bean02 中调用 Bean01 的 hello 方法时,Bean01 只是执行了构造函数,bean02 属性还没有注入进去。如果在 Bean01 的 hello 方法中调用一个spring bean 的方法,会失败。

测试5: Spring 有限支持循环调用

首先,增加一个 Bean03 类:

package demo.bean;

import org.springframework.stereotype.Component;

@Component
public class Bean03 {

	public String hi() {
		return "hi";
	}
}

改造 Bean01.java :

// ......
@Component
public class Bean01 implements InitializingBean, ApplicationContextAware, ApplicationListener<ApplicationEvent> {

	@Autowired
	private Bean02 bean02;
	@Autowired
	private Bean03 bean03;

	public Bean01() {
		Utils.log("%s construct", getClassName());
	}

	public String hello() {
		bean03.hi();  // 这里会抛出 NullPointerException 异常
		return String.format("%s hello", getClassName());
	}

	@PostConstruct
	public void postConstruct() {
		Utils.log("%s @PostConstruct. bean01.hello(): %s", getClassName(), bean02.hello());
	}
    
    // ......

改造 Bean02.java :

// ...
@Component
public class Bean02 implements InitializingBean, ApplicationContextAware, ApplicationListener<ApplicationEvent> {

	@Autowired
	private Bean01 bean01;

	public Bean02() {
		Utils.log("%s construct", getClassName());
	}

	public String hello() {
		return String.format("%s hello", getClassName());
	}

	@PostConstruct
	public void postConstruct() {
		Utils.log("%s @PostConstruct. bean01.hello(): %s", getClassName(), bean01.hello());
	}

    // ...

运行 Demo004 ,会报错:

[main@1          ][17:40:51.665] Bean01 construct
[main@1          ][17:40:51.688] Bean02 construct
[main@1          ][17:40:51.690] Bean02 ApplicationContextAware.setApplicationContext: org.springframework.context.annotation.AnnotationConfigApplicationContext@2dda6444, started on Sat Feb 15 17:40:51 CST 2020
二月 15, 2020 5:40:51 下午 org.springframework.context.support.AbstractApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'bean01': Unsatisfied dependency expressed through field 'bean02'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bean02': Invocation of init method failed; nested exception is java.lang.NullPointerException
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'bean01': Unsatisfied dependency expressed through field 'bean02'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bean02': Invocation of init method failed; nested exception is java.lang.NullPointerException
    // ... 省略部分内容
Caused by: java.lang.NullPointerException
	at demo.bean.Bean01.hello(Bean01.java:28)
	at demo.bean.Bean02.postConstruct(Bean02.java:31)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:389)
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:333)
	at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:157)
	... 26 more

为什么在 Bean02 的 postConstruct 方法中调用 Bean01 的 hello 方法会抛出异常?

因为此时 Bean01 实例只执行了构造函数,还没有把 bean03 注入进来,也就是 bean03 是 null。所以 Bean01 的 hello 方法中 bean03.hi() 会抛出空指针异常。

原理

AbstractBeanFactory -> AbstractAutowireCapableBeanFactory: createBean
AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory: doCreateBean

doCreateBean 流程:

AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory: createBeanInstance
AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory: populateBean
AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory: invokeAwareMethods
AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory: applyBeanPostProcessorsBeforeInitialization
AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory: invokeInitMethods
AbstractAutowireCapableBeanFactory -> AbstractAutowireCapableBeanFactory: applyBeanPostProcessorsAfterInitialization

createBeanInstance 方法

使用构造函数或者工厂函数生成实例。

populateBean 方法

注入 @Autowired 注解的字段的实例。会去生成其他 bean 的实例。

若两个bean之间通过 @Autowired 产生了循环依赖,是能正常生成的。以 Bean01、Bean02 为例:

  1. Bean01 基于构造函数,生成实例。
  2. 发现 Bean01 中 @Autowired 了 Bean02,于是尝试初始化 Bean02。
  3. Bean02 基于构造函数,生成实例。
  4. 此时,Bean01 因为是有实例的,所以可以注入到 Bean02 中。
  5. Bean02 初始化完成后,继续初始化Bean01 。

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

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

getBeanPostProcessors() 是一个 List<BeanPostProcessor> 列表,它们的 postProcessBeforeInitialization 方法分别做了这些事:

  1. ApplicationContextAwareProcessor : 依次执行以下接口对应的方法 EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、MessageSourceAware、ApplicationContextAware
  2. ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor : 执行 ImportAware 接口的方法
  3. PostProcessorRegistrationDelegate$BeanPostProcessorChecker : 什么都没做
  4. CommonAnnotationBeanPostProcessor : 执行 @PostConstruct 注解的方法,等等
  5. AutowiredAnnotationBeanPostProcessor : 什么都没做
  6. ApplicationListenerDetector : 什么都没做

invokeInitMethods 方法

若实现了 InitializingBean 接口,则执行 afterPropertiesSet 方法。

applyBeanPostProcessorsAfterInitialization 方法

和 applyBeanPostProcessorsBeforeInitialization 类似。但执行的是 postProcessAfterInitialization 方法。

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

getBeanPostProcessors() 是一个 List<BeanPostProcessor> 列表,它们的 postProcessAfterInitialization 方法分别做了这些事:

  1. ApplicationContextAwareProcessor : 什么都没做
  2. ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor : 什么都没做
  3. PostProcessorRegistrationDelegate$BeanPostProcessorChecker : 什么都没做
  4. CommonAnnotationBeanPostProcessor : 什么都没做
  5. AutowiredAnnotationBeanPostProcessor : 什么都没做
  6. ApplicationListenerDetector : 若实现了 ApplicationListenerDetector 接口,将当前bean 加入 applicationContext 的 listener 集合中。

ApplicationListener 的 onApplicationEvent 方法

spring 容器把所有 bean 初始化完成后,会通过 AbstractApplicationContext 发布 ContextRefreshedEvent 事件。

具体逻辑见 AbstractApplicationContext # refresh 方法的最后一步 finishRefresh 。


( 本文完 )