學習Spring源碼的建議 閱讀Spring官方文檔,瞭解Spring框架的基本概念和使用方法。 下載Spring源碼,可以從官網或者GitHub上獲取。 閱讀Spring源碼的入口類,瞭解Spring框架的啟動過程和核心組件的載入順序。 閱讀Spring源碼中的註釋和文檔,瞭解每個類和方法的作用和 ...
學習Spring源碼的建議
-
閱讀Spring官方文檔,瞭解Spring框架的基本概念和使用方法。
-
下載Spring源碼,可以從官網或者GitHub上獲取。
-
閱讀Spring源碼的入口類,瞭解Spring框架的啟動過程和核心組件的載入順序。
-
閱讀Spring源碼中的註釋和文檔,瞭解每個類和方法的作用和用法。
-
調試Spring源碼,可以通過IDEA等工具進行調試,瞭解Spring框架的內部實現和運行過程。
-
參考Spring源碼的測試用例,瞭解Spring框架的各個組件的使用方法和測試方法。
-
參考Spring源碼的設計模式和最佳實踐,瞭解如何設計和實現高質量的Java應用程式。
-
參與Spring社區,與其他開發者交流和分享經驗,瞭解Spring框架的最新動態和發展趨勢。
學習Spring源碼的好處
-
更深入地瞭解Spring框架的內部實現和運行機制,可以更好地理解和使用Spring框架。
-
學習Spring源碼可以提高自己的編程能力和代碼質量,瞭解Spring框架的設計模式和最佳實踐,可以應用到自己的項目中。
-
學習Spring源碼可以幫助開發者解決一些複雜的問題和難點,提高自己的解決問題的能力。
-
學習Spring源碼可以幫助開發者更好地理解Java語言和麵向對象編程的思想,提高自己的編程水平。
-
學習Spring源碼可以幫助開發者更好地瞭解Java生態系統和相關技術,如AOP、IOC、MVC等。
-
學習Spring源碼可以幫助開發者更好地瞭解開源軟體的開發和維護過程,提高自己的開源軟體開發能力。
refresh方法所出現的問題和異常
最近抽空總結一下之前通用的Spring框架所出現的問題和異常情況,當創建屬於自己的ApplicationContext對象的時候,經常會遇到這麼幾條異常消息:
- LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context: ......
LifecycleProcessor對象沒有初始化,在調用context的生命周期方法之前必須調用'refresh'方法。
- BeanFactory not initialized or already closed - call 'refresh' before accessing beans via the ApplicationContext
BeanFactory對象沒有初始化或已經關閉了,使用ApplicationContext獲取Bean之前必須調用'refresh'方法。
- ApplicationEventMulticaster not initialized - call 'refresh' before multicasting events via the context: ......
ApplicationEventMulticaster對象沒有初始化,在context廣播事件之前必須調用'refresh'方法。
這幾條異常消息都與refresh方法有關,那拋出這些異常的原因到底是什麼,為什麼在這麼多情況下一定要先調用refresh方法(定義在AbstractApplicationContext類中),在此這前我們先看看refresh方法中又幹了些什麼?
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
LifecycleProcessor not initialized - call 'refresh' before invoking lifecycle methods via the context:
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。
obtainFreshBeanFactory
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 {
//如果beanFactory已經不為null,則銷毀beanFactory中的Bean後自行關閉
if (hasBeanFactory()) {
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。
initApplicationEventMulticaster
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();
}
對於第一條異常消息,異常堆棧出錯在applicationContext.start();下麵是start()方法源碼:
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
可以看到start()方法中要先獲取lifecycleProcessor對象,而預設構造方法中並沒用調用refresh方法,所以lifecycleProcessor為null,故而在getLifecycleProcessor()方法中拋出了此異常消息。
異常的測試案例(2)
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
applicationContext.setConfigLocation("application-context.xml");
applicationContext.getBean("xtayfjpk");
applicationContext.close();
}
第二條異常消息,異常堆棧出錯在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,因此拋出異常。
異常的測試案例(3)
public static void main(String[] args) {
GenericApplicationContext parent = new GenericApplicationContext();
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setParent(parent);
context.refresh();
context.start();
context.close();
}
這其中提到了生命周期方法,其實就是定義在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();
}
}
第三條異常消息,異常堆棧出錯在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()方法,其它情況則會自動調用。
本文來自博客園,作者:洛神灬殤,轉載請註明原文鏈接:https://www.cnblogs.com/liboware/p/17347682.html,任何足夠先進的科技,都與魔法無異。