Spring的refresh()方法相關異常
阿新 • • 發佈:2019-01-06
如果是經常使用Spring,特別有自己新建ApplicationContext物件的經歷的人,肯定見過這麼幾條異常訊息:
1.LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context: ......
2.BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
3.ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: ......
第一條訊息是說LifecycleProcessor物件沒有初始化,在呼叫context的生命週期方法之前必須呼叫'refresh'方法
第二條訊息是說BeanFactory物件沒有初始化或已經關閉了,使用ApplicationContext獲取Bean之前必須呼叫'refresh'方法
第三條訊息是說ApplicationEventMulticaster物件沒有初始化,在context廣播事件之前必須呼叫'refresh'方法
這幾條異常訊息都與refresh方法有關,那丟擲這些異常的原因到底是什麼,為什麼在這麼多情況下一定要先呼叫refresh方法(定義在AbstractApplicationContext類中), 在此這前我們先看看refresh方法中又幹了些什麼?
與此三條異常訊息相關的方法分別為:finishRefresh();obtainFreshBeanFactory();initApplicationEventMulticaster();
如果沒有呼叫finishRefresh方法,則lifecycleProcessor成員為null。
refreshBeanFactory()為一抽象方法,真正實現在AbstractRefreshableApplicationContext類中:
如果沒有呼叫obtainFreshBeanFactory()方法則beanFactory成員為null。
而這三個方法呼叫都在refresh()方法中,由上面的分析可知,如果沒有呼叫refresh方法,則上下文中的lifecycleProcessor,beanFactory,applicationEventMulticaster成員都會為null。至此可以來詳細分析這三條異常訊息的緣由了。
下面是針對上面三條異常訊息的三段測試程式碼,順序相對應:
對於第一條異常訊息,異常堆疊出錯在applicationContext.start();下面是start()方法原始碼:
可以看到start()方法中要先獲取lifecycleProcessor物件,而預設構造方法中並沒用呼叫refresh方法,所以lifecycleProcessor為null,故而在getLifecycleProcessor()方法中丟擲了此異常訊息。這其中提到了生命週期方法,其實就是定義在org.springframework.context.Lifecycle介面中的start(), stop(), isRunning()三個方法,如果是剛開始學習Spring的話,建立ClassPathXmlApplicationContext物件時應該是這樣的:ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");這樣直接呼叫start()方法卻又不會出現異常,這是為什麼呢?這是因為ClassPathXmlApplicationContext(String configLocation)這個構造方法最終呼叫的是:
由於ClassPathXmlApplicationContext的預設構造方法沒有呼叫refresh()方法,所以beanFactory為null,因此丟擲異常。
從上面可以看到:如果父上下文不為null,則還需要呼叫父容器的pushlishEvent方法,而且在該方法中呼叫了getApplicationEventMulticaster()方法以獲取一個事件廣播器,問題就出現在這裡:
而applicationEventMulticaster就是在refresh方法中的initApplicationEventMulticaster方法在例項化的,則於父上下文沒有呼叫過refresh方法所以父上下文的applicationEventMulticaster成員為null,因此丟擲異常。
1.LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context: ......
2.BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
3.ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: ......
第一條訊息是說LifecycleProcessor物件沒有初始化,在呼叫context的生命週期方法之前必須呼叫'refresh'方法
第二條訊息是說BeanFactory物件沒有初始化或已經關閉了,使用ApplicationContext獲取Bean之前必須呼叫'refresh'方法
第三條訊息是說ApplicationEventMulticaster物件沒有初始化,在context廣播事件之前必須呼叫'refresh'方法
這幾條異常訊息都與refresh方法有關,那丟擲這些異常的原因到底是什麼,為什麼在這麼多情況下一定要先呼叫refresh方法(定義在AbstractApplicationContext類中),
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { //重新整理之前的準備工作,包括設定啟動時間,是否啟用標識位,初始化屬性源(property source)配置 prepareRefresh(); //由子類去重新整理BeanFactory(如果還沒建立則建立),並將BeanFactory返回 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //準備BeanFactory以供ApplicationContext使用 prepareBeanFactory(beanFactory); try { //子類可通過格式此方法來對BeanFactory進行修改 postProcessBeanFactory(beanFactory); //例項化並呼叫所有註冊的BeanFactoryPostProcessor物件 invokeBeanFactoryPostProcessors(beanFactory); //例項化並呼叫所有註冊的BeanPostProcessor物件 registerBeanPostProcessors(beanFactory); //初始化MessageSource initMessageSource(); //初始化事件廣播器 initApplicationEventMulticaster(); //子類覆蓋此方法在重新整理過程做額外工作 onRefresh(); //註冊應用監聽器ApplicationListener registerListeners(); //例項化所有non-lazy-init bean finishBeanFactoryInitialization(beanFactory); //重新整理完成工作,包括初始化LifecycleProcessor,釋出重新整理完成事件等 finishRefresh(); } catch (BeansException ex) { // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } } }
與此三條異常訊息相關的方法分別為:finishRefresh();obtainFreshBeanFactory();initApplicationEventMulticaster();
protected void finishRefresh() { // //初始化LifecycleProcessor initLifecycleProcessor(); // Propagate refresh to lifecycle processor first. getLifecycleProcessor().onRefresh(); // Publish the final event. publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active. LiveBeansView.registerApplicationContext(this); }
如果沒有呼叫finishRefresh方法,則lifecycleProcessor成員為null。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();//重新整理BeanFactory,如果beanFactory為null,則建立
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
refreshBeanFactory()為一抽象方法,真正實現在AbstractRefreshableApplicationContext類中:
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {//如果beanFactory已經不為null,則銷燬beanFactory中的Bean後自行關閉
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();//建立beanFactory
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;//對beanFactory成員進行賦值
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
如果沒有呼叫obtainFreshBeanFactory()方法則beanFactory成員為null。
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isDebugEnabled()) {
logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
"': using default [" + this.applicationEventMulticaster + "]");
}
}
}
而這三個方法呼叫都在refresh()方法中,由上面的分析可知,如果沒有呼叫refresh方法,則上下文中的lifecycleProcessor,beanFactory,applicationEventMulticaster成員都會為null。至此可以來詳細分析這三條異常訊息的緣由了。
下面是針對上面三條異常訊息的三段測試程式碼,順序相對應:
1. public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
applicationContext.setConfigLocation("application-context.xml");
applicationContext.start();
applicationContext.close();
}
2. public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
applicationContext.setConfigLocation("application-context.xml");
applicationContext.getBean("xtayfjpk");
applicationContext.close();
}
3. public static void main(String[] args) {
GenericApplicationContext parent = new GenericApplicationContext();
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setParent(parent);
context.refresh();
context.start();
context.close();
}
對於第一條異常訊息,異常堆疊出錯在applicationContext.start();下面是start()方法原始碼:
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
可以看到start()方法中要先獲取lifecycleProcessor物件,而預設構造方法中並沒用呼叫refresh方法,所以lifecycleProcessor為null,故而在getLifecycleProcessor()方法中丟擲了此異常訊息。這其中提到了生命週期方法,其實就是定義在org.springframework.context.Lifecycle介面中的start(), stop(), isRunning()三個方法,如果是剛開始學習Spring的話,建立ClassPathXmlApplicationContext物件時應該是這樣的:ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");這樣直接呼叫start()方法卻又不會出現異常,這是為什麼呢?這是因為ClassPathXmlApplicationContext(String configLocation)這個構造方法最終呼叫的是:
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {//refresh傳遞值為true,這樣就自動呼叫了refresh方法進行了重新整理
refresh();
}
}
第二條異常訊息,異常堆疊出錯在applicationContext.getBean("xtayfjpk"),applicationContext.getBean()方法呼叫的是上下文中beanFactory的getBean()方法實現的,獲取BeanFactory物件的程式碼在其基類ConfigurableListableBeanFactory中的getBeanFactory()方法中:
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
synchronized (this.beanFactoryMonitor) {
if (this.beanFactory == null) {
throw new IllegalStateException("BeanFactory not initialized or already closed - " +
"call 'refresh' before accessing beans via the ApplicationContext");
}
return this.beanFactory;
}
}
由於ClassPathXmlApplicationContext的預設構造方法沒有呼叫refresh()方法,所以beanFactory為null,因此丟擲異常。
第三條異常訊息,異常堆疊出錯在context.refresh(),但是如果沒有設定父上下文的話context.setParent(parent),例子程式碼是不會出現異常的。這是因為在refresh方法中的finishRefresh()方法呼叫了publishEvent方法:
public void publishEvent(ApplicationEvent event) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
getApplicationEventMulticaster().multicastEvent(event);
if (this.parent != null) {
this.parent.publishEvent(event);
}
}
從上面可以看到:如果父上下文不為null,則還需要呼叫父容器的pushlishEvent方法,而且在該方法中呼叫了getApplicationEventMulticaster()方法以獲取一個事件廣播器,問題就出現在這裡:
private ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
if (this.applicationEventMulticaster == null) {//如果為null則拋異常
throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
"call 'refresh' before multicasting events via the context: " + this);
}
return this.applicationEventMulticaster;
}
而applicationEventMulticaster就是在refresh方法中的initApplicationEventMulticaster方法在例項化的,則於父上下文沒有呼叫過refresh方法所以父上下文的applicationEventMulticaster成員為null,因此丟擲異常。
綜上所述,其實這三條異常訊息的根本原因只有一個,就是當一個上下文物件建立後沒有呼叫refresh()方法。在Spring中ApplicationContext實現類有很多,有些實現類在建立的過程中自動呼叫了refresh()方法,而有些又沒有,如果沒有則需要自己手動呼叫refresh()方法。一般說來實現WebApplicationContext介面的實現類以及使用預設構造方法建立上下文物件時不會自動refresh()方法,其它情況則會自動呼叫。