前言
- 在沒有使用Spring前業務處理過程中業務對象都是通過關鍵字new來創建,雖然可用 static 方法、工具包等形式做一些優化但是還是有局限性。如使用new創建的對象在編譯期定死了使用那個對象不能像Spring那樣可用在運行期決定使用那個對象。再比如在業務處理類中我們可以定義一個接口,在不同的邏輯情況下從Spring容器中獲得不同的實現類來處理邏輯,普通情況下我們可能要new 一堆的對象等待使用(看著實在頭大)。這就是“針對接口編程,而不是針對實現編程”帶來的好處。
Spring通過配置把業務對象托管給Spring容器,這樣我們就不用在手動創建對象了需要用那個對象(默認為單例singleton)直接從Spring容器中取即可。元數據配置如:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
<bean id="pxx_test_bean" name="yourMessageSource"
class="org.springframework.context.support.StaticMessageSource"/>
<bean id="pxx_test_user" class="org.springframework.context.support.User">
<property name="name" value="平行線"></property>
</bean>
</beans>
- Spring容器簡單介紹圖如下:
通過ClassPathXmlApplicationContext源碼查看容器如何實現
如果還不清楚如何導入源碼可以參考我之前分享的GIT + GRADLE+Eclipse 導入Spring 源碼文章。
- 首先看一下ClassPathXmlApplicationContext結構,如下圖所示:
- ClassPathXmlApplicationContext 的構造函數如下
- 使用關鍵字new創建一個容器代碼如:
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
- ClassPathXmlApplicationContext構造函數 如下:
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
//調用ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)構造函數
this(configLocations, true, null);
}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
/**該構造函數主要是設置容器中用戶業務對象的xml配置、
*Spring容器中ClassLoader、刷新容器獲得
*內置的工廠類DefaultListableBeanFactory等操作
*/
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
refresh(); 方法詳解
- refresh()方法代碼如下:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
}
}
prepareRefresh();
主要是上下文的初始值刷新。如啟動時間(startupDate)、當前容器是否已經關閉的標志位(closed初始值為:false),上下文是活躍狀態(active初始值為:true)等。ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
為容器創建內部的bean工廠。容器中的業務對象由bean工廠管理。如從bean工廠中獲得一個對象代碼片段如下:
public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
return doGetBean(name, requiredType, args, false);
}
prepareBeanFactory(beanFactory);
設置bean工廠的類加載器(ClassLoader)、設置bean工廠的bean表達式解析器(StandardBeanExpressionResolver)、環境變量如jdk版本等信息等操作postProcessBeanFactory(beanFactory);
在容器標準化初始化后修改內部的bean工廠。在所有的業務對象配置都加載,但沒有實例化做特殊處理ClassPathXmlApplicationContext中為空實現。invokeBeanFactoryPostProcessors(beanFactory);
在容器完成標準初始化后,在所有bean定義都加載了,但是這些bean還沒有被初始化的情況下如果容器中有添加了接口BeanFactoryPostProcessor的實現類那么他們都會在單例實例化前被調用。
- initMessageSource();
初始化用于保存容器中的格式化好的消息資源。如果容器中存在對應的消息資源這不再重新創建。如容器可以通過先獲得消息實現類如DelegatingMessageSource再通過getMssege()方法獲得最終格式化的數據如"{0}", "{1,date}", "{2,time}等等。
下圖示例DelegatingMessageSource 中的消息最終以key-value 的形式返回調用者。
- initApplicationEventMulticaster();
初始化容器事件管理器,比如容器的事件添加事件移除事件等見如下代碼片段
//添加事件
void addApplicationListener(ApplicationListener<?> listener);
//移除事件
void removeApplicationListener(ApplicationListener<?> listener);
onRefresh();
ClassPathXmlApplicationContext中為空實現。registerListeners();
向事件管理器中注冊事件完成特殊邏輯。finishBeanFactoryInitialization(beanFactory);
示例化剩余的單例對象除了延遲加載(lazy-init="true")的業務對象外 。finishRefresh();
啟動容器的最后步驟。主要做的4件事情是1:初始化生命周期的處理器。2:刷新生命周期處理器。3:執行事件管理器中注冊的事件。4:如果容器中的環境變量存在“spring.liveBeansView.mbeanDomain” 那么當前應用會保存一份當前容器的一份快照(3.1 或者更高版本)
代碼片段如下:
protected void finishRefresh() {
initLifecycleProcessor();
getLifecycleProcessor().onRefresh();
publishEvent(new ContextRefreshedEvent(this));
LiveBeansView.registerApplicationContext(this);
}
總結
通過前面的代碼我們知道了一個以xml配置文件啟動的容器是啟動的全過程,并且可以通過容器獲得不同類型(如全新的,單例的)的業務對象。當然還有很多細節需要詳細探究。比如通過關鍵字 new創建的對象在jvm中是有它的生命周期的,當然Spring容器中的內置bean工廠(接口BeanFactory 的實現類 如 DefaultListableBeanFactory 類)也有一套自己的生命周期(lifecycle)。接下來的文章會一一詳解。
希望對看到這篇文章的小伙伴有所幫助,如果有描述不正確的地方希望不吝賜教。