容器初始化之FileSystemXmlApplicationContext建構函式
宅男Coder,沒有其他愛好,閒暇之餘抱著瞻仰的心態去閱讀一下Spring的原始碼,期許能收穫一支半解。要學習Spring的原始碼,第一步自然是下載和編譯Spring的原始碼,這個我在之前的博文中已經發表過了。具體可參考:《SpringFramework原始碼下載和編譯教程》
面對茫茫多的Spring的工程和程式碼,很多人可能會無從下手。其實想想,Spring也是有入口的,那就是配置檔案的載入。Spring容器的構建完全是基於配置檔案的配置的。不論是Web工程,還是普通的Java應用,載入Spring配置檔案都是首要的工作。所以,我就從配置檔案的載入學起。
要載入配置檔案,首先當然是要找到該檔案。大多數人通常都是在Web應用中使用Spring。網上搜搜配置,配置檔案的名字就叫約定的:applicationContext.xml,然後往編譯路徑下一扔,Spring自然就好用了,就沒過多的關注過其他容器初始化的問題。其實,一個自然應該想到的問題就是:一個普通的J2SE應用該如何使用Spring呢?答案很簡單:new 出一個ApplicationContext
private static final String SPRINT_FILEPATH_CONTEXT = "D:\\workspace-home\\OpenSourceStudy\\src\\main\\resources\\spring\\app-context.xml";
ApplicationContext appContext = new FileSystemXmlApplicationContext(
SPRINT_FILEPATH_CONTEXT);
只需上述一行程式碼,一個基於指定的配置檔案的Spring容器就初始化完成了。
下面,我們來仔細看看FileSystemXmlApplicationContext這個類:
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
/**
* Create a new FileSystemXmlApplicationContext for bean-style configuration.
* @see #setConfigLocation
* @see #setConfigLocations
* @see #afterPropertiesSet()
*/
public FileSystemXmlApplicationContext() {
}
/**
* Create a new FileSystemXmlApplicationContext for bean-style configuration.
* @param parent the parent context
* @see #setConfigLocation
* @see #setConfigLocations
* @see #afterPropertiesSet()
*/
public FileSystemXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
/**
* Create a new FileSystemXmlApplicationContext, loading the definitions
* from the given XML file and automatically refreshing the context.
* @param configLocation file path
* @throws BeansException if context creation failed
*/
public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
/**
* Create a new FileSystemXmlApplicationContext, loading the definitions
* from the given XML files and automatically refreshing the context.
* @param configLocations array of file paths
* @throws BeansException if context creation failed
*/
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}
/**
* Create a new FileSystemXmlApplicationContext with the given parent,
* loading the definitions from the given XML files and automatically
* refreshing the context.
* @param configLocations array of file paths
* @param parent the parent context
* @throws BeansException if context creation failed
*/
public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
this(configLocations, true, parent);
}
/**
* Create a new FileSystemXmlApplicationContext, loading the definitions
* from the given XML files.
* @param configLocations array of file paths
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @throws BeansException if context creation failed
* @see #refresh()
*/
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
this(configLocations, refresh, null);
}
/**
* Create a new FileSystemXmlApplicationContext with the given parent,
* loading the definitions from the given XML files.
* @param configLocations array of file paths
* @param refresh whether to automatically refresh the context,
* loading all bean definitions and creating all singletons.
* Alternatively, call refresh manually after further configuring the context.
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
/**
* Resolve resource paths as file system paths.
* <p>Note: Even if a given path starts with a slash, it will get
* interpreted as relative to the current VM working directory.
* This is consistent with the semantics in a Servlet container.
* @param path path to the resource
* @return Resource handle
* @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath
*/
@Override
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
}
一共七個建構函式和一個複寫的方法。我們現在重點關注建構函式,除前兩個之外,其他的建構函式都最終指向建構函式
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
只是對沒有傳入的引數,給了一個預設值。
該建構函式有三個引數:
- String[] configLocations - 配置檔案的路徑陣列。是陣列也就是說,支援同時傳入多個配置檔案路徑。
- boolean refresh - 是否重新整理,如果是true,則會開始初始化Spring容器, false則暫時不初始化Spring容器。
- ApplicationContext parent - Spring容器上下文。即可以傳入已經初始化過的Spring容器,新初始化的容器會包含parent上下文中的內容,例如:父容器中定義的bean等。
refresh()方法可謂Spring的核心的入口函式,Spring容器的初始化正是由此開始。一些Spring學習的書中,也向學習Spring的讀者推薦,如果感到無所適從,可從該方法入手,研究Spring的整個生命週期。後續我們也會從該方法入手重點研究。現在只需理解,其為Spring容器初始化的一個“開關”即可。
前兩個建構函式與該建構函式最大的區別就是,沒有呼叫refresh函式。也就是說,Spring容器,此時並未初始化。此時如果用getBean方法去獲取Bean的例項,會報容器並未初始化的異常。
下面給出一些關於建構函式的測試用例,能更直觀、具體的說明問題
private String filePath = "D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\app-context.xml";
private String parentFilePath = "D:\\workspace-home\\spring-custom\\src\\main\\resources\\spring\\parent-context.xml";
/**
* 直接指定一個單一的配置檔案初始化Spring容器。容器中包含該指定配置檔案中定義的Bean{@code VeryCommonBean}。
*
* @author lihzh
* @date 2012-4-29 下午9:39:27
*/
@Test
public void testFileContextWithPath() {
ApplicationContext appContext = new FileSystemXmlApplicationContext(
filePath);
assertNotNull("The app context should not be null.", appContext);
assertNotNull(appContext.getBean(VeryCommonBean.class));
}
/**
* 將已經初始化好的Spring容器上下文傳遞給新的Spring容器。需手動呼叫容器的重新整理
* {@code ConfigurableApplicationContext#refresh()}(初始化)。
*
* @author lihzh
* @date 2012-5-4 下午9:28:40
*/
@Test
public void testFileContextWithParentOnly() {
ApplicationContext parentContext = new FileSystemXmlApplicationContext(
parentFilePath);
ConfigurableApplicationContext appContext = new FileSystemXmlApplicationContext(
parentContext);
assertNotNull("The parent context should not be null.", parentContext);
assertNotNull(appContext);
// 需要手動重新整理(初始化)容器
appContext.refresh();
assertNotNull(parentContext.getBean(ParentBean.class));
assertNotNull(appContext.getBean(ParentBean.class));
}
/**
* 根據指定的配置檔案和已經初始化好的Parent容器上下文,例項化一個新的Spring容器,該容器中包含這兩部分的資訊。
*
* @author lihzh
* @date 2012-5-4 下午10:07:20
*/
@Test
public void testFileContextWithParentAndItself() {
ApplicationContext parentContext = new FileSystemXmlApplicationContext(
parentFilePath);
ApplicationContext appContext = new FileSystemXmlApplicationContext(
new String[] { filePath }, parentContext);
assertNotNull("The parent context should not be null", parentContext);
assertNotNull(appContext);
assertTrue("Should not have bean:[VeryCommonBean] in parent context.",
!parentContext.containsBean("VeryCommonBean"));
assertNotNull(parentContext.getBean(ParentBean.class));
assertNotNull(appContext.getBean(ParentBean.class));
assertNotNull("Should have bean:[VeryCommonBean].",
appContext.getBean("VeryCommonBean"));
}
/**
* 同時指定多個配置檔案來初始化Spring容器
*
* @author lihzh
* @date 2012-5-4 下午10:09:02
*/
@Test
public void testFileContextWithMultiPath() {
ApplicationContext appContext = new FileSystemXmlApplicationContext(
parentFilePath, filePath);
assertNotNull(appContext);
assertNotNull(appContext.getBean(ParentBean.class));
assertNotNull("Should have bean:[VeryCommonBean].",
appContext.getBean(VeryCommonBean.class));
}
瞭解這些有什麼用?
- 學會在一個普通的J2SE應用中使用Spring,初始化Spring容器。
- 瞭解初始化Spring容器的幾種方式和傳入的引數,可配合不同的場景使用。例如:在程式執行期動態初始化一個新的Spring容器,包含已經初始化的容器,即可用與parent相關的建構函式。再例如:在可能的特定的場景下,Spring容器需要在特定的實際初始化,可先呼叫空建構函式,再傳入location,再手動refresh。或者直接給refresh傳入false,再手動refresh。再比如:配合Spring的foo包,可監控Spring配置檔案的變化,利用refresh函式,實時更新Spring容器等等。