Spring的refresh()方法相關(guān)異常解析
Spring是一個開放源代碼的設(shè)計層面框架,他解決的是業(yè)務(wù)邏輯層和其他各層的松耦合問題,因此它將面向接口的編程思想貫穿整個系統(tǒng)應(yīng)用。Spring是于2003 年興起的一個輕量級的Java 開發(fā)框架,由Rod Johnson創(chuàng)建。簡單來說,Spring是一個分層的JavaSE/EEfull-stack(一站式) 輕量級開源框架。
如果是經(jīng)常使用Spring,特別有自己新建ApplicationContext對象的經(jīng)歷的人,肯定見過這么幾條異常消息:
1.LifecycleProcessornotinitialized-call'refresh'beforeinvokinglifecyclemethodsviathecontext:......
2.BeanFactorynotinitializedoralreadyclosed-call'refresh'beforeaccessingbeansviatheApplicationContext
3.ApplicationEventMulticasternotinitialized-call'refresh'beforemulticastingeventsviathecontext:......
第一條消息是說LifecycleProcessor對象沒有初始化,在調(diào)用context的生命周期方法之前必須調(diào)用'refresh'方法
第二條消息是說BeanFactory對象沒有初始化或已經(jīng)關(guān)閉了,使用ApplicationContext獲取Bean之前必須調(diào)用'refresh'方法
第三條消息是說ApplicationEventMulticaster對象沒有初始化,在context廣播事件之前必須調(diào)用'refresh'方法
這幾條異常消息都與refresh方法有關(guān),那拋出這些異常的原因到底是什么,為什么在這么多情況下一定要先調(diào)用refresh方法(定義在AbstractApplicationContext類中),在此這前我們先看看refresh方法中又干了些什么?
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//刷新之前的準(zhǔn)備工作,包括設(shè)置啟動時間,是否激活標(biāo)識位,初始化屬性源(property source)配置
prepareRefresh();
//由子類去刷新BeanFactory(如果還沒創(chuàng)建則創(chuàng)建),并將BeanFactory返回
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//準(zhǔn)備BeanFactory以供ApplicationContext使用
prepareBeanFactory(beanFactory);
try {
//子類可通過格式此方法來對BeanFactory進(jìn)行修改
postProcessBeanFactory(beanFactory);
//實例化并調(diào)用所有注冊的BeanFactoryPostProcessor對象
invokeBeanFactoryPostProcessors(beanFactory);
//實例化并調(diào)用所有注冊的BeanPostProcessor對象
registerBeanPostProcessors(beanFactory);
//初始化MessageSource
initMessageSource();
//初始化事件廣播器
initApplicationEventMulticaster();
//子類覆蓋此方法在刷新過程做額外工作
onRefresh();
//注冊應(yīng)用監(jiān)聽器ApplicationListener
registerListeners();
//實例化所有non-lazy-init bean
finishBeanFactoryInitialization(beanFactory);
//刷新完成工作,包括初始化LifecycleProcessor,發(fā)布刷新完成事件等
finishRefresh();
}
catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
與此三條異常消息相關(guān)的方法分別為:
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);
}
如果沒有調(diào)用finishRefresh方法,則lifecycleProcessor成員為null。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();//刷新BeanFactory,如果beanFactory為null,則創(chuàng)建
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
refreshBeanFactory()為一抽象方法,真正實現(xiàn)在AbstractRefreshableApplicationContext類中:
@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {//如果beanFactory已經(jīng)不為null,則銷毀beanFactory中的Bean后自行關(guān)閉
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();//創(chuàng)建beanFactory
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;//對beanFactory成員進(jìn)行賦值
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
如果沒有調(diào)用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 + "]");
}
}
}
而這三個方法調(diào)用都在refresh()方法中,由上面的分析可知,如果沒有調(diào)用refresh方法,則上下文中的lifecycleProcessor,beanFactory,applicationEventMulticaster成員都會為null。至此可以來詳細(xì)分析這三條異常消息的緣由了。
下面是針對上面三條異常消息的三段測試代碼,順序相對應(yīng):
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對象,而默認(rèn)構(gòu)造方法中并沒用調(diào)用refresh方法,所以lifecycleProcessor為null,故而在getLifecycleProcessor()方法中拋出了此異常消息。這其中提到了生命周期方法,其實就是定義在org.springframework.context.Lifecycle接口中的start(),stop(),isRunning()三個方法,如果是剛開始學(xué)習(xí)Spring的話,創(chuàng)建ClassPathXmlApplicationContext對象時應(yīng)該是這樣的:ClassPathXmlApplicationContextapplicationContext=newClassPathXmlApplicationContext("application-context.xml");這樣直接調(diào)用start()方法卻又不會出現(xiàn)異常,這是為什么呢?這是因為ClassPathXmlApplicationContext(StringconfigLocation)這個構(gòu)造方法最終調(diào)用的是:
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {//refresh傳遞值為true,這樣就自動調(diào)用了refresh方法進(jìn)行了刷新
refresh();
}
}
第二條異常消息,異常堆棧出錯在applicationContext.getBean("xtayfjpk"),applicationContext.getBean()方法調(diào)用的是上下文中beanFactory的getBean()方法實現(xiàn)的,獲取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的默認(rèn)構(gòu)造方法沒有調(diào)用refresh()方法,所以beanFactory為null,因此拋出異常。
第三條異常消息,異常堆棧出錯在context.refresh(),但是如果沒有設(shè)置父上下文的話context.setParent(parent),例子代碼是不會出現(xiàn)異常的。這是因為在refresh方法中的finishRefresh()方法調(diào)用了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,則還需要調(diào)用父容器的pushlishEvent方法,而且在該方法中調(diào)用了getApplicationEventMulticaster()方法以獲取一個事件廣播器,問題就出現(xiàn)在這里:
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方法在實例化的,則于父上下文沒有調(diào)用過refresh方法所以父上下文的applicationEventMulticaster成員為null,因此拋出異常。
綜上所述,其實這三條異常消息的根本原因只有一個,就是當(dāng)一個上下文對象創(chuàng)建后沒有調(diào)用refresh()方法。在Spring中ApplicationContext實現(xiàn)類有很多,有些實現(xiàn)類在創(chuàng)建的過程中自動調(diào)用了refresh()方法,而有些又沒有,如果沒有則需要自己手動調(diào)用refresh()方法。一般說來實現(xiàn)WebApplicationContext接口的實現(xiàn)類以及使用默認(rèn)構(gòu)造方法創(chuàng)建上下文對象時不會自動refresh()方法,其它情況則會自動調(diào)用。
總結(jié)
以上就是本文關(guān)于Spring的refresh()方法相關(guān)異常的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站:
Spring AOP攔截-三種方式實現(xiàn)自動代理詳解
如有不足之處,歡迎留言指出。
相關(guān)文章
淺談Java當(dāng)作數(shù)組的幾個應(yīng)用場景
數(shù)組可以存放多個同一類型的數(shù)據(jù),可以存儲基本數(shù)據(jù)類型,引用數(shù)據(jù)類型(對象),下面這篇文章主要給大家介紹了關(guān)于Java當(dāng)作數(shù)組的幾個應(yīng)用場景,需要的朋友可以參考下2022-11-11
詳解JDBC的概念及獲取數(shù)據(jù)庫連接的5種方式
Java?DataBase?Connectivity是將Java與SQL結(jié)合且獨立于特定的數(shù)據(jù)庫系統(tǒng)的應(yīng)用程序編程接口,一種可用于執(zhí)行SQL語句的JavaAPI。本文主要介紹了JDBC的概念及獲取數(shù)據(jù)庫連接的5種方式,需要的可以參考一下2022-09-09
如何使用BigDecimal實現(xiàn)Java開發(fā)商業(yè)計算
這篇文章主要介紹了如何使用BigDecimal實現(xiàn)Java開發(fā)商業(yè)計算,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-09-09
Java基于解釋器模式實現(xiàn)定義一種簡單的語言功能示例
這篇文章主要介紹了Java基于解釋器模式實現(xiàn)定義一種簡單的語言功能,簡單描述了解釋器模式的概念、功能及Java使用解釋器模式定義一種簡單語言的相關(guān)實現(xiàn)與使用技巧,需要的朋友可以參考下2018-05-05
Java?Lambda表達(dá)式常用的函數(shù)式接口
這篇文章主要介紹了Java?Lambda表達(dá)式常用的函數(shù)式接口,文章基于Java?Lambda表達(dá)式展開對常用的函數(shù)式接口的介紹,具有一的的參考價值需要的小伙伴可以參考一下2022-04-04
Spring與Shiro整合及加載權(quán)限表達(dá)式問題
這篇文章主要介紹了Spring與Shiro整合及加載權(quán)限表達(dá)式問題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-12-12

