问题概述
想通过 @PostConstruct
实现一些 bean 初始化之后的逻辑,但同时注解的方法中有用到了一个静态类,类中使用如下伪代码:
private static final XxxBean XXX_BEAN = SpringContextUtils.getBean(XxxBean.class);
这就带来了一个问题,没法保证当前类初始化之前,XxxBean已经初始化完成了(当然可能有其他方式,这里不做赘述)。
解决方案
此时可以通过实现 ApplicationListener<ContextRefreshedEvent>
接口,并实现 onApplicationEvent
方法解决。
也就是在bean全部初始化完成之后,spring会自动调用这个方法。
示例代码如下:
@Component public class InitFinishNotice implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { // logic... } }
二次调用问题
在传统 spring mvc 模式下,这里存在重复调用的问题,需要特殊一下。
打个断点,看一下 ContextRefreshedEvent 是什么?
org.springframework.context.event.ContextRefreshedEvent[source=Root WebApplicationContext, started on Tue Mar 09 13:44:10 CST 2021]
org.springframework.context.event.ContextRefreshedEvent[source=WebApplicationContext for namespace 'spring-servlet', started on Tue Mar 09 13:46:07 CST 2021, parent: Root WebApplicationContext]
第一次是Root WebApplicationContext
,第二次是WebApplicationContext for namespace 'spring-servlet'
,它的父级是Root WebApplicationContext
。所以可以通过 event.getApplicationContext().getParent() != null
区分一下,来避免重复调用。
示例代码如下:
@Component public class InitFinishNotice implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { if(event.getApplicationContext().getParent() != null){ return; } // logic... } }
源码调用简述
AbstractApplicationContext#refresh()
finishRefresh();
publishEvent(new ContextRefreshedEvent(this));
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
SimpleApplicationEventMulticaster#multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType)
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { invokeListener(listener, event)) }
doInvokeListener(listener, event);