Spring IOC體系結(jié)構(gòu)
(1) BeanFactory
Spring Bean的創(chuàng)建是典型的工廠模式,這一系列的Bean工廠,也即IOC容器為開發(fā)者管理對象間的依賴關(guān)系提供了很多便利和基礎(chǔ)服務(wù),在Spring中有許多的IOC容器的實(shí)現(xiàn)供用戶選擇和使用,其相互關(guān)系如下:

其中BeanFactory作為最頂層的一個(gè)接口類,它定義了IOC容器的基本功能規(guī)范,BeanFactory 有三個(gè)子類:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。但是從上圖中我們可以發(fā)現(xiàn)最終的默認(rèn)實(shí)現(xiàn)類是 DefaultListableBeanFactory,他實(shí)現(xiàn)了所有的接口。那為何要定義這么多層次的接口呢?查閱這些接口的源碼和說明發(fā)現(xiàn),每個(gè)接口都有他使用的場合,它主要是為了區(qū)分在 Spring 內(nèi)部在操作過程中對象的傳遞和轉(zhuǎn)化過程中,對對象的數(shù)據(jù)訪問所做的限制。例如 ListableBeanFactory 接口表示這些 Bean 是可列表的,而 HierarchicalBeanFactory 表示的是這些 Bean 是有繼承關(guān)系的,也就是每個(gè)Bean 有可能有父 Bean。AutowireCapableBeanFactory 接口定義 Bean 的自動裝配規(guī)則。這四個(gè)接口共同定義了 Bean 的集合、Bean 之間的關(guān)系、以及 Bean 行為
最基本的IOC容器接口BeanFactory:
1 public interface BeanFactory {
2
3 //對FactoryBean的轉(zhuǎn)義定義,因?yàn)槿绻褂胋ean的名字檢索FactoryBean得到的對象是工廠生成的對象,
4 //如果需要得到工廠本身,需要轉(zhuǎn)義
5 String FACTORY_BEAN_PREFIX = "&";
6
7 //根據(jù)bean的名字,獲取在IOC容器中得到bean實(shí)例
8 Object getBean(String name) throws BeansException;
9
10 //根據(jù)bean的名字和Class類型來得到bean實(shí)例,增加了類型安全驗(yàn)證機(jī)制。
11 Object getBean(String name, Class requiredType) throws BeansException;
12
13 //提供對bean的檢索,看看是否在IOC容器有這個(gè)名字的bean
14 boolean containsBean(String name);
15
16 //根據(jù)bean名字得到bean實(shí)例,并同時(shí)判斷這個(gè)bean是不是單例
17 boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
18
19 //得到bean實(shí)例的Class類型
20 Class getType(String name) throws NoSuchBeanDefinitionException;
21
22 //得到bean的別名,如果根據(jù)別名檢索,那么其原名也會被檢索出來
23 String[] getAliases(String name);
在BeanFactory里只對IOC容器的基本行為作了定義,根本不關(guān)心你的bean是如何定義怎樣加載的。正如我們只關(guān)心工廠里得到什么的產(chǎn)品對象,至于工廠是怎么生產(chǎn)這些對象的,這個(gè)基本的接口不關(guān)心。 而要知道工廠是如何產(chǎn)生對象的,我們需要看具體的IOC容器實(shí)現(xiàn),spring提供了許多IOC容器的實(shí)現(xiàn)。
比如:XmlBeanFactory,ClasspathXmlApplicationContext等。其中XmlBeanFactory就是針對最基本的ioc容器的實(shí)現(xiàn),這個(gè)IOC容器可以讀取XML文件定義的BeanDefinition(XML文件中對bean的描述),如果說XmlBeanFactory是容器中的屌絲,ApplicationContext應(yīng)該算容器中的高帥富。ApplicationContext是Spring提供的一個(gè)高級的IoC容器,它除了能夠提供IoC容器的基本功能外,還為用戶提供了以下的附加服務(wù)。
從ApplicationContext接口的實(shí)現(xiàn),我們看出其特點(diǎn):
1.支持信息源,可以實(shí)現(xiàn)國際化(實(shí)現(xiàn)MessageSource接口)
2.訪問資源 (實(shí)現(xiàn)ResourcePatternResolver接口,這個(gè)后面要講)
3.支持應(yīng)用事件 (實(shí)現(xiàn)ApplicationEventPublisher接口)
(2) BeanDefinition
SpringIOC容器管理了我們定義的各種Bean對象及其相互的關(guān)系,Bean對象在Spring實(shí)現(xiàn)中是以BeanDefinition來描述的,其繼承體系如下:

Bean 的解析過程非常復(fù)雜,功能被分的很細(xì),因?yàn)檫@里需要被擴(kuò)展的地方很多,必須保證有足夠的靈活性,以應(yīng)對可能的變化。Bean 的解析主要就是對 Spring 配置文件的解析。這個(gè)解析過程主要通過下圖中的類完成:

IoC容器的初始化
IoC容器的初始化包括BeanDefinition的Resource定位、載入和注冊這三個(gè)基本的過程。我們以ApplicationContext為例講解,ApplicationContext系列容器也許是我們最熟悉的,因?yàn)閣eb項(xiàng)目中使用的XmlWebApplicationContext就屬于這個(gè)繼承體系,還有ClasspathXmlApplicationContext等,其繼承體系如下圖所示:

ApplicationContext允許上下文嵌套,通過保持父上下文可以維持一個(gè)上下文體系。對于bean的查找可以在這個(gè)上下文體系中發(fā)生,首先檢查當(dāng)前上下文,其次是父上下文,逐級向上,這樣為不同的Spring應(yīng)用提供了一個(gè)共享的bean定義環(huán)境。
下面我們分別簡單地演示一下兩種ioc容器的創(chuàng)建過程
1、XmlBeanFactory(屌絲IOC)的整個(gè)流程
通過XmlBeanFactory的源碼,我們可以發(fā)現(xiàn):
public class XmlBeanFactory extends DefaultListableBeanFactory{
private final XmlBeanDefinitionReader reader;
public XmlBeanFactory(Resource resource)throws BeansException{
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
throws BeansException{
super(parentBeanFactory);
this.reader = new XmlBeanDefinitionReader(this);
this.reader.loadBeanDefinitions(resource);
}
}
//根據(jù)Xml配置文件創(chuàng)建Resource資源對象,該對象中包含了BeanDefinition的信息
ClassPathResource resource =new ClassPathResource("application-context.xml");
//創(chuàng)建DefaultListableBeanFactory
DefaultListableBeanFactory factory =new DefaultListableBeanFactory();
//創(chuàng)建XmlBeanDefinitionReader讀取器,用于載入BeanDefinition。之所以需要BeanFactory作為參數(shù),是因?yàn)闀⒆x取的信息回調(diào)配置給factory
XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);
//XmlBeanDefinitionReader執(zhí)行載入BeanDefinition的方法,最后會完成Bean的載入和注冊。完成后Bean就成功的放置到IOC容器當(dāng)中,以后我們就可以從中取得Bean來使用
reader.loadBeanDefinitions(resource);
this.reader = new XmlBeanDefinitionReader(this); 中其中this 傳的是factory對象
2、FileSystemXmlApplicationContext 的IOC容器流程
1.ApplicationContext =new FileSystemXmlApplicationContext(xmlPath);
先看其構(gòu)造函數(shù):
/**
* 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);
}
實(shí)際調(diào)用
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
2、設(shè)置資源加載器和資源定位
通過分析FileSystemXmlApplicationContext的源代碼可以知道,在創(chuàng)建FileSystemXmlApplicationContext容器時(shí),構(gòu)造方法做以下兩項(xiàng)重要工作:
首先,調(diào)用父類容器的構(gòu)造方法(super(parent)方法)為容器設(shè)置好Bean資源加載器。
然后,再調(diào)用父類AbstractRefreshableConfigApplicationContext的setConfigLocations(configLocations)方法設(shè)置Bean定義資源文件的定位路徑。
通過追蹤FileSystemXmlApplicationContext的繼承體系,發(fā)現(xiàn)其父類的父類AbstractApplicationContext中初始化IoC容器所做的主要源碼如下:
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
//靜態(tài)初始化塊,在整個(gè)容器創(chuàng)建過程中只執(zhí)行一次
static {
//為了避免應(yīng)用程序在Weblogic8.1關(guān)閉時(shí)出現(xiàn)類加載異常加載問題,加載IoC容
//器關(guān)閉事件(ContextClosedEvent)類
ContextClosedEvent.class.getName();
}
//FileSystemXmlApplicationContext調(diào)用父類構(gòu)造方法調(diào)用的就是該方法
public AbstractApplicationContext(ApplicationContext parent) {
this.parent = parent;
this.resourcePatternResolver = getResourcePatternResolver();
}
//獲取一個(gè)Spring Source的加載器用于讀入Spring Bean定義資源文件
protected ResourcePatternResolver getResourcePatternResolver() {
// AbstractApplicationContext繼承DefaultResourceLoader,也是一個(gè)S
//Spring資源加載器,其getResource(String location)方法用于載入資源
return new PathMatchingResourcePatternResolver(this);
}
……
}
AbstractApplicationContext構(gòu)造方法中調(diào)用PathMatchingResourcePatternResolver的構(gòu)造方法創(chuàng)建Spring資源加載器:
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
//設(shè)置Spring的資源加載器
this.resourceLoader = resourceLoader;
}
在設(shè)置容器的資源加載器之后,接下來FileSystemXmlApplicationContet執(zhí)行setConfigLocations方法通過調(diào)用其父類AbstractRefreshableConfigApplicationContext的方法進(jìn)行對Bean定義資源文件的定位,該方法的源碼如下:
//處理單個(gè)資源文件路徑為一個(gè)字符串的情況
public void setConfigLocation(String location) {
//String CONFIG_LOCATION_DELIMITERS = ",; /t/n";
//即多個(gè)資源文件路徑之間用” ,; /t/n”分隔,解析成數(shù)組形式
setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
}
//解析Bean定義資源文件的路徑,處理多個(gè)資源文件字符串?dāng)?shù)組
public void setConfigLocations(String[] locations) {
if (locations != null) {
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for (int i = 0; i < locations.length; i++) {
// resolvePath為同一個(gè)類中將字符串解析為路徑的方法
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
通過這兩個(gè)方法的源碼我們可以看出,我們既可以使用一個(gè)字符串來配置多個(gè)Spring Bean定義資源文件,也可以使用字符串?dāng)?shù)組,即下面兩種方式都是可以的:
a. ClasspathResource res = new ClasspathResource(“a.xml,b.xml,……”);
多個(gè)資源文件路徑之間可以是用” ,; /t/n”等分隔。
b. ClasspathResource res = new ClasspathResource(newString[]{“a.xml”,”b.xml”,……});
至此,Spring IoC容器在初始化時(shí)將配置的Bean定義資源文件定位為Spring封裝的Resource。
3、AbstractApplicationContext的refresh函數(shù)載入Bean定義過程
Spring IoC容器對Bean定義資源的載入是從refresh()函數(shù)開始的,refresh()是一個(gè)模板方法,refresh()方法的作用是:在創(chuàng)建IoC容器前,如果已經(jīng)有容器存在,則需要把已有的容器銷毀和關(guān)閉,以保證在refresh之后使用的是新建立起來的IoC容器。refresh的作用類似于對IoC容器的重啟,在新建立好的容器中對容器進(jìn)行初始化,對Bean定義資源進(jìn)行載入
FileSystemXmlApplicationContext通過調(diào)用其父類AbstractApplicationContext的refresh()函數(shù)啟動整個(gè)IoC容器對Bean定義的載入過程:
1 public void refresh() throws BeansException, IllegalStateException {
2 synchronized (this.startupShutdownMonitor) {
3 //調(diào)用容器準(zhǔn)備刷新的方法,獲取容器的當(dāng)時(shí)時(shí)間,同時(shí)給容器設(shè)置同步標(biāo)識
4 prepareRefresh();
5 //告訴子類啟動refreshBeanFactory()方法,Bean定義資源文件的載入從
6 //子類的refreshBeanFactory()方法啟動
7 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
8 //為BeanFactory配置容器特性,例如類加載器、事件處理器等
9 prepareBeanFactory(beanFactory);
10 try {
11 //為容器的某些子類指定特殊的BeanPost事件處理器
12 postProcessBeanFactory(beanFactory);
13 //調(diào)用所有注冊的BeanFactoryPostProcessor的Bean
14 invokeBeanFactoryPostProcessors(beanFactory);
15 //為BeanFactory注冊BeanPost事件處理器.
16 //BeanPostProcessor是Bean后置處理器,用于監(jiān)聽容器觸發(fā)的事件
17 registerBeanPostProcessors(beanFactory);
18 //初始化信息源,和國際化相關(guān).
19 initMessageSource();
20 //初始化容器事件傳播器.
21 initApplicationEventMulticaster();
22 //調(diào)用子類的某些特殊Bean初始化方法
23 onRefresh();
24 //為事件傳播器注冊事件監(jiān)聽器.
25 registerListeners();
26 //初始化所有剩余的單態(tài)Bean.
27 finishBeanFactoryInitialization(beanFactory);
28 //初始化容器的生命周期事件處理器,并發(fā)布容器的生命周期事件
29 finishRefresh();
30 }
31 catch (BeansException ex) {
32 //銷毀以創(chuàng)建的單態(tài)Bean
33 destroyBeans();
34 //取消refresh操作,重置容器的同步標(biāo)識.
35 cancelRefresh(ex);
36 throw ex;
37 }
38 }
39 }
refresh()方法主要為IoC容器Bean的生命周期管理提供條件,Spring IoC容器載入Bean定義資源文件從其子類容器的refreshBeanFactory()方法啟動,所以整個(gè)refresh()中“ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();”這句以后代碼的都是注冊容器的信息源和生命周期事件,載入過程就是從這句代碼啟動。
refresh()方法的作用是:在創(chuàng)建IoC容器前,如果已經(jīng)有容器存在,則需要把已有的容器銷毀和關(guān)閉,以保證在refresh之后使用的是新建立起來的IoC容器。refresh的作用類似于對IoC容器的重啟,在新建立好的容器中對容器進(jìn)行初始化,對Bean定義資源進(jìn)行載入
AbstractApplicationContext的obtainFreshBeanFactory()方法調(diào)用子類容器的refreshBeanFactory()方法,啟動容器載入Bean定義資源文件的過程,代碼如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//這里使用了委派設(shè)計(jì)模式,父類定義了抽象的refreshBeanFactory()方法,具體實(shí)現(xiàn)調(diào)用子類容器的refreshBeanFactory()方法
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
AbstractApplicationContext子類的refreshBeanFactory()方法:
AbstractApplicationContext類中只抽象定義了refreshBeanFactory()方法,容器真正調(diào)用的是其子類AbstractRefreshableApplicationContext實(shí)現(xiàn)的 refreshBeanFactory()方法,方法的源碼如下:
1 protected final void refreshBeanFactory() throws BeansException {
2 if (hasBeanFactory()) {//如果已經(jīng)有容器,銷毀容器中的bean,關(guān)閉容器
3 destroyBeans();
4 closeBeanFactory();
5 }
6 try {
7 //創(chuàng)建IoC容器
8 DefaultListableBeanFactory beanFactory = createBeanFactory();
9 beanFactory.setSerializationId(getId());
10 //對IoC容器進(jìn)行定制化,如設(shè)置啟動參數(shù),開啟注解的自動裝配等
11 customizeBeanFactory(beanFactory);
12 //調(diào)用載入Bean定義的方法,主要這里又使用了一個(gè)委派模式,在當(dāng)前類中只定義了抽象的loadBeanDefinitions方法,具體的實(shí)現(xiàn)調(diào)用子類容器
13 loadBeanDefinitions(beanFactory);
14 synchronized (this.beanFactoryMonitor) {
15 this.beanFactory = beanFactory;
16 }
17 }
18 catch (IOException ex) {
19 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
20 }
21 }
在這個(gè)方法中,先判斷BeanFactory是否存在,如果存在則先銷毀beans并關(guān)閉beanFactory,接著創(chuàng)建DefaultListableBeanFactory,并調(diào)用loadBeanDefinitions(beanFactory)裝載bean定義。
5、AbstractRefreshableApplicationContext子類的loadBeanDefinitions方法:
AbstractRefreshableApplicationContext中只定義了抽象的loadBeanDefinitions方法,容器真正調(diào)用的是其子類AbstractXmlApplicationContext對該方法的實(shí)現(xiàn),AbstractXmlApplicationContext的主要源碼如下:
loadBeanDefinitions方法同樣是抽象方法,是由其子類實(shí)現(xiàn)的,也即在AbstractXmlApplicationContext中。
1 public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
2 ……
3 //實(shí)現(xiàn)父類抽象的載入Bean定義方法
4 @Override
5 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
6 //創(chuàng)建XmlBeanDefinitionReader,即創(chuàng)建Bean讀取器,并通過回調(diào)設(shè)置到容器中去,容 器使用該讀取器讀取Bean定義資源
7 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
8 //為Bean讀取器設(shè)置Spring資源加載器,AbstractXmlApplicationContext的
9 //祖先父類AbstractApplicationContext繼承DefaultResourceLoader,因此,容器本身也是一個(gè)資源加載器
10 beanDefinitionReader.setResourceLoader(this);
11 //為Bean讀取器設(shè)置SAX xml解析器
12 beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
13 //當(dāng)Bean讀取器讀取Bean定義的Xml資源文件時(shí),啟用Xml的校驗(yàn)機(jī)制
14 initBeanDefinitionReader(beanDefinitionReader);
15 //Bean讀取器真正實(shí)現(xiàn)加載的方法
16 loadBeanDefinitions(beanDefinitionReader);
17 }
18 //Xml Bean讀取器加載Bean定義資源
19 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
20 //獲取Bean定義資源的定位
21 Resource[] configResources = getConfigResources();
22 if (configResources != null) {
23 //Xml Bean讀取器調(diào)用其父類AbstractBeanDefinitionReader讀取定位
24 //的Bean定義資源
25 reader.loadBeanDefinitions(configResources);
26 }
27 //如果子類中獲取的Bean定義資源定位為空,則獲取FileSystemXmlApplicationContext構(gòu)造方法中setConfigLocations方法設(shè)置的資源
28 String[] configLocations = getConfigLocations();
29 if (configLocations != null) {
30 //Xml Bean讀取器調(diào)用其父類AbstractBeanDefinitionReader讀取定位
31 //的Bean定義資源
32 reader.loadBeanDefinitions(configLocations);
33 }
34 }
35 //這里又使用了一個(gè)委托模式,調(diào)用子類的獲取Bean定義資源定位的方法
36 //該方法在ClassPathXmlApplicationContext中進(jìn)行實(shí)現(xiàn),對于我們
37 //舉例分析源碼的FileSystemXmlApplicationContext沒有使用該方法
38 protected Resource[] getConfigResources() {
39 return null;
40 } ……
41}