Spring 版本: 5.2.2.RELEASE
先上结论,顺序是:
- 构造函数
依赖的bean被初始化完成,并注入到了当前bean 中
(循环依赖时,稍微不同)- ApplicationContextAware 接口的 setApplicationContext 方法
- @PostConstruct 注解的方法
- InitializingBean 接口的 afterPropertiesSet 方法
- 等所有 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,内部的执行顺序是:
- 构造函数
- ApplicationContextAware 接口的 setApplicationContext 方法
- @PostConstruct 注解的方法
- InitializingBean 接口的 afterPropertiesSet 方法
- 等所有 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]
由此,可以得出更细致的结论:
- 构造函数
依赖的bean被初始化完成,并注入到了当前bean 中
- ApplicationContextAware 接口的 setApplicationContext 方法
- @PostConstruct 注解的方法
- InitializingBean 接口的 afterPropertiesSet 方法
- 等所有 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 为例:
- Bean01 基于构造函数,生成实例。
- 发现 Bean01 中 @Autowired 了 Bean02,于是尝试初始化 Bean02。
- Bean02 基于构造函数,生成实例。
- 此时,Bean01 因为是有实例的,所以可以注入到 Bean02 中。
- 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 方法分别做了这些事:
- ApplicationContextAwareProcessor : 依次执行以下接口对应的方法 EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、MessageSourceAware、ApplicationContextAware
- ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor : 执行 ImportAware 接口的方法
- PostProcessorRegistrationDelegate$BeanPostProcessorChecker : 什么都没做
- CommonAnnotationBeanPostProcessor : 执行 @PostConstruct 注解的方法,等等
- AutowiredAnnotationBeanPostProcessor : 什么都没做
- 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 方法分别做了这些事:
- ApplicationContextAwareProcessor : 什么都没做
- ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor : 什么都没做
- PostProcessorRegistrationDelegate$BeanPostProcessorChecker : 什么都没做
- CommonAnnotationBeanPostProcessor : 什么都没做
- AutowiredAnnotationBeanPostProcessor : 什么都没做
- ApplicationListenerDetector : 若实现了 ApplicationListenerDetector 接口,将当前bean 加入 applicationContext 的 listener 集合中。
ApplicationListener
的 onApplicationEvent 方法
spring 容器把所有 bean 初始化完成后,会通过 AbstractApplicationContext 发布 ContextRefreshedEvent 事件。
具体逻辑见 AbstractApplicationContext # refresh 方法的最后一步 finishRefresh 。