參考《Spring技術內幕》一書:
IoC容器的基本接口是由BeanFactory來定義的,也就是說BeanFactory定義了IoC容器的最基本的形式,並且提供了 IoC容器所應該遵守的最基本的服務契約。BeanFactory只是一個接口類,並沒有給出容器的具體實現。DefaultListableBeanFactory,XmlBeanFactory,ApplicationContext,FileSystemXmlBeanFactory,ClassPathXmlBeanFactory都實現了BeanFactory接口並且擴展了IoC容器的功能。
首先介紹BeanFactory:
public interface BeanFactory {
//這裏是對FactoryBean的轉義定義,因爲如果使用bean的名字檢索FactoryBean得到的對象是工廠生成的對象,
//如果需要得到工廠本身,需要轉義
String FACTORY_BEAN_PREFIX = "&";
//這裏根據bean的名字,在IOC容器中得到bean實例,這個IOC容器就是一個大的抽象工廠。
Object getBean(String name) throws BeansException;
//這裏根據bean的名字和Class類型來得到bean實例,和上面的方法不同在於它會拋出異常:如果根據名字取得的bean實例的Class類型和需要的不同的話。
Object getBean(String name, Class requiredType) throws BeansException;
//這裏提供對bean的檢索,看看是否在IOC容器有這個名字的bean
boolean containsBean(String name);
//這裏根據bean名字得到bean實例,並同時判斷這個bean是不是單件
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//這裏對得到bean實例的Class類型
Class getType(String name) throws NoSuchBeanDefinitionException;
//這裏得到bean的別名,如果根據別名檢索,那麼其原名也會被檢索出來
String[] getAliases(String name);
}
用戶使用容器時,可以使用轉義字符'&'來得到FactoryBean本身,用來區分通過容器來獲取FactoryBean產生的對象還是獲取FactoryBean本身。在Spring中所有的Bean都是由BeanFactory來管理的,而對於FactoryBean,它是一個能產生或者修飾對象生成的工廠Bean。
BeanFactory和FactoryBean:BeanFactory它指的是IoC容器的編程抽象,而FactoryBean指的是一個抽象工廠,對它的調用返回的是工廠產生的對象,而不是它本身。
我們先通過編程實現IoC容器:
public class UserBeanFatory {
public static void main(String[] args) {
//創建一個BeanFactory,這裏使用DefaultListableBeanFactory,包含IoC容器的重要功能
DefaultListableBeanFactory factory=new DefaultListableBeanFactory();
/*
* 創建一個載入BeanDefinition的讀取器,這裏使用XmlBeanDefinitionReader來載入XML文件形式的
* BeanDefinition,使用一個回調配置給BeanFactory
*/
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);
/*
* 創建Ioc配置文件的抽象資源,這個抽象資源中包含了BeanDefinition的定義信息
*/
ClassPathResource res=new ClassPathResource("applicationContext-beans.xml");
/*
* 從定義好的資源位置讀入配置信息,具體的解析過程是由XmlBeanDefinitionReader
* 來完成的。完成整個的載入與註冊Bean定義之後,需要的IoC容器就建立起來了
*/
reader.loadBeanDefinitions(res);
User user=(User)factory.getBean("user");
System.out.println(user.getUsername()+":"+user.getPassword());
//等價於
XmlBeanFactory xmlfactory=new XmlBeanFactory(new ClassPathResource("applicationContext-beans.xml"));
User xmluser=(User)factory.getBean("user");
System.out.println(xmluser.getUsername()+":"+xmluser.getPassword());
ApplicationContext ac=new FileSystemXmlApplicationContext("D:/java/kcsj/SourceXmpBeanFactory/src/applicationContext-beans.xml");
ac.getBean("user");
}
}
由上面我們可以想到IoC 容器初始化分爲三個步驟:
1 BeanDefinition的Resource定位
2 BeanDefinition的載入和解析
3 BeanDefinition的註冊
我們先看BeanDefinition的Resource定位:
下面以FileSystemXmlApplicationContext爲例,通過分析這個ApplicationContext的實現來看看它是怎樣完成Resource的定位的。
ApplicationContext ac=new FileSystemXmlApplicationContext("D:/java/kcsj/SourceXmpBeanFactory/src/applicationContext-beans.xml");
我們首先看看FileSystemXmlApplicationContext的源碼:
public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
.....
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
}
在FileSystemXmlApplicationContext 的構造函數中完成了兩部分功能:1是設置BeanDefinition的配置文件的路徑,是的所有在配置文件中的BeanDefinition都能得到有效地處理;2 就是通過refresh()方法啓動了IoC容器的初始化。
AbstractApplicationContext的refresh()方法源碼解析:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
它包含了IoC容器的整個初始化的過程,包括:BeanFactory 的更新,初始化messagesource,配置和註冊後置處理器,註冊監聽器和事件觸發器,還有進行預實例化(non-lazy-init)的處理等等。它把資源的定位交給了obtainFreshBeanFactory方法:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
然後又通過調用抽象方法refreshBeanFactory,它的實現在AbstractRefreshableApplicaitonContext中:
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
這裏先判斷是否已經建立了BeanFactory,如果建立則銷燬並關閉BeanFactory,然後創建BeanFactory,這裏創建的是DefaultListableBeanFactory,然後調用loadBeanDefinitions載入BeanDefinition的配置信息。接着我們去看loadBeanDefinitions方法的具體執行過程:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
這裏它先創建一個BeanDefinition的Xml讀取器,並且回調配置給BeanFactory,如同我們前面通過編程實現IoC容器的初始化,然後再轉到loadBeanDefinitions(beanDefintionReader)中:
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
它首先獲得BeanDefinition的配置文件的資源,判斷是否存在,如果存在在加載,然後獲取配置文件的路徑,判斷是否存在,如果存在則加載。一種是從資源中加載,另一種是從給定的路徑中加載。由於我們在沒有顯式的定義資源,我們只是給定了一個配置文件的路徑,所以它會從路徑加載。也就是調用reader.loadBeanDefinitions(configLocations)方法。
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}
然後繼續調用loadBeanDefinitions(location)方法:
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
再轉到:loadBeanDefinitions(String location, Set<Resource> actualResources)方法中
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}
Resource resource = resourceLoader.getResource(location);就是用來定位BeanDefinition的資源的,它會交給DefaultResourceLoader的getResource()方法處理:
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
最後它又調用getResourceByPath(location),它被FileSystemXmlApplicationContext中getResourceByPath()覆蓋了,具體源碼如下:
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
到這裏爲止,IoC容器的初始化的第一步驟已經完成了,總結可得BeanDefinition的Resource的定位是通過DefaultResourceLoader來getResource()方法來定位的,在getResource()中又調用getResourceByPath(),它被不同的BeanFactory覆蓋。