問題概述
想通過 @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);
