一、IOC接口設計
IOC容器設計的源碼主要在spring-beans.jar、spring-context.jar這兩個包中。IOC容器主要接口設計如下:

這里的接口設計有兩條主線:BeanFactory和ApplicationContext
1、BeanFactory-->HierarchicalBeanFactory-->ConfigurableBeanFactory:這是BeanFactory的設計路線,BeanFactory定義了基本的IOC容器規范,HierarchicalBeanFactory中增加了getParentBeanFactory方法,具備了雙親IOC容器的管理功能;ConfigurableBeanFactory中新增一些配置功能。
2、ApplicationContext應用上下文接口:繼承了HierarchicalBeanFactory、ListableBeanFactory等BeanFactory的子接口,這條分支使得ApplicationContext具備了IOC容器的基本功能;在繼承MessageSource、ApplicationEventPublisher等接口的時候,使得ApplicationContext這個簡單的IOC容器添加了許多高級容器的特性。ApplicationContext的子接口有ConfigurableApplicationContext以及在WEB環境下使用的WebApplicationContext。
二、BeanFactory的設計原理
public abstract interface BeanFactory
{
public static final String FACTORY_BEAN_PREFIX = "&";
public abstract Object getBean(String paramString)
throws BeansException;
public abstract <T> T getBean(String paramString, Class<T> paramClass)
throws BeansException;
public abstract <T> T getBean(Class<T> paramClass)
throws BeansException;
public abstract Object getBean(String paramString, Object[] paramArrayOfObject)
throws BeansException;
public abstract boolean containsBean(String paramString);
public abstract boolean isSingleton(String paramString)
throws NoSuchBeanDefinitionException;
public abstract boolean isPrototype(String paramString)
throws NoSuchBeanDefinitionException;
public abstract boolean isTypeMatch(String paramString, Class<?> paramClass)
throws NoSuchBeanDefinitionException;
public abstract Class<?> getType(String paramString)
throws NoSuchBeanDefinitionException;
public abstract String[] getAliases(String paramString);
}
BeanFactory只是定義了IOC容器的基本輪廓,並沒有給出容器的具體實現(這個后面詳細介紹)。
先來討論下BeanFactory和FactoryBean之間的區別
1、前者很好理解,就是Spring的一個類工廠,用它可以創建各種類型的Bean,最主要的方法就是getBean(String paramString)。而創建的各種類型的Bean中有一種比較特殊的Bean就是FactoryBean。
2、Spring容器中管理里兩種Bean,一種是標准的Java Bean,從容器中獲取的是類本身的實例;另外一種就是FactoryBean即工廠Bean,從容器中獲取Bean的時候,返回的並不是類的一個實例,而是工廠Bean中getObject方法返回的對象。工廠Bean必須實現接口FactoryBean。
工廠Bean---->SayHelloFactoryBeanImpl
public class SayHelloFactoryBeanImpl implements FactoryBean { public Object getObject() throws Exception { return new UserBean(); } public Class getObjectType() { return UserBean.class; } public boolean isSingleton() { return false; } }
工廠Bean返回的對象:UserBean
public class UserBean { public void show() { System.out.println("春天來了"); } }
Spring配置文件:
<bean id="sayHelloBean" class="SayHelloFactoryBeanImpl"></bean>
測試類:
ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Object bean = ctx.getBean("sayHelloBean"); Object bean1 = ctx.getBean("&sayHelloBean"); System.out.println(bean); System.out.println(bean1);
執行結果:
UserBean@7225790e
SayHelloFactoryBeanImpl@54a097cc
從正常的情況下,從容器中獲取ID為“sayHelloBean”的對象應該是SayHelloFactoryBeanImpl。但由於SayHelloFactoryBeanImpl實現了接口FactoryBean,這是一個工廠Bean,所以通過ID獲取到的Bean是SayHelloFactoryBeanImpl類中getObject方法返回的對象。如果要獲取FactoryBean自身的一個實例,必須通過&+BeanID的形式去獲取。
網上有許多對工廠Bean總結歸納,如:工廠Bean是實現了FactoryBean接口的bean 它不是一個簡單的Bean 而是一個生產或修飾對象生成的工廠Bean。這里我先Mark一下:為什么Spring要設計這種類型的Bean。
三、XmlBeanFactory的解讀

XmlBeanFactory是IOC容器系列最底層的實現,它繼承自DefaultListableBeanFactory這個類。而后者是Spring中非常重要的一個類,它是Spring容器中一個基本產品,可以把它當做一個默認的功能完整的IOC容器來使用。
XmlBeanFactory除了從DefaultListableBeanFactory繼承到IOC容器基本功能之外,還新增了一些其他功能,從名稱就可以猜測出來,它是一個可以讀取以XML文件方式定義BeanDefinition的容器。
XmlBeanFactory源碼如下:
1 public class XmlBeanFactory extends DefaultListableBeanFactory 2 { 3 private final XmlBeanDefinitionReader reader; 4 5 public XmlBeanFactory(Resource resource) 6 throws BeansException 7 { 8 this(resource, null); 9 } 10 11 public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) 12 throws BeansException 13 { 14 super(parentBeanFactory); 15 16 this.reader = new XmlBeanDefinitionReader(this); 17 18 this.reader.loadBeanDefinitions(resource); 19 } 20 }
實際上,實現XML讀取功能並不是直接由XmlBeanFactory來完成的。而是由XmlBeanFactory內部定義的XmlBeanDefinitionReader來進行處理的。在構造XmlBeanFactory容器的時候,需要給出BeanDefinition的信息來源,而這個信息來源需要封裝成Spring中的Resource類的形式給出。
來看下一個基本的IOC容器的初始化過程:
1、創建IOC配置文件的抽象資源,也就是源碼中的Resource,這個Resource中包含了BeanDefinition的定義信息。
2、通過構造函數創建一個BeanFactory。
3、創建一個載入BeanDefinition的讀取器,即源碼中的reader。這里使用XmlBeanDefinitionReader來載入XML文件形式的BeanDefinition。
4、調用reader的loadBeanDefinitions方法,來完成從Resource中載入BeanDefinition信息,從而完成IOC容器的初始化。
我們可以將上面的源碼做簡化,使用編程式的方式來表達IOC容器的初始化:

總結:DefaultListableBeanFactory是IOC容器的一個基類,XmlBeanFactory是在其基礎上擴展而來的。而其他的IOC容器,例如ApplicationContext,它的實現原理和XmlBeanFactory類似,也是通過擴展DefaultListableBeanFactory來獲取基本的IOC容器功能的。
四、ApplicationContext的設計原理
接口設計圖:

1、ApplicationContext繼承接口ListableBeanFactory、HierarchicalBeanFactory,實現了IOC容器的基本功能。
2、繼承接口MessageSource:支持不同信息源,支持國際化的實現。
3、繼承接口ResourceLoader:支持該容器可以從不同I/O途徑得到Bean的定義信息。
4、繼承接口ApplicationEventPublisher:在上下文中引入了事件機制。這些事件機制和Bean的生命周期結合為Bean的管理提供了便利。
ApplicationContext增加了這些附加功能,使得基本IOC容器的功能更加豐富,所以建議在開發應用的時候使用ApplicationContext作為IOC容器的基本形式。
ApplicationContext的設計原理
以子類FileSystemXmlApplicationContext的實現為例說明其設計原理。接口設計圖如下:

這個接口設計中,ApplicationContext應用上下文的主要功能已經在FileSystemXmlApplicationContext的基類AbstractXmlApplicationContext中實現了,而FileSystemXmlApplicationContext作為一個具體的IOC容器,只需要實現和其本身相關的功能即可。
源碼片段:
1 public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) 2 throws BeansException 3 { 4 super(parent); 5 setConfigLocations(configLocations); 6 if (refresh) 7 refresh(); 8 } 9 10 protected Resource getResourceByPath(String path) 11 { 12 if ((path != null) && (path.startsWith("/"))) { 13 path = path.substring(1); 14 } 15 return new FileSystemResource(path); 16 } 17 }
這里面有兩個主要的方法:refresh()、getResourceByPath(String path)
1、refresh涉及到IOC容器啟動的一系列操作,由於這個啟動過程對於不同類型的容器來說都是相似的,所以這個啟動過程被封裝在基類中,具體的容器只需要調用即可。refresh方法后面會有詳細介紹。
2、getResourceByPath這個方法是跟FileSystemXmlApplicationContext區別於其他具體容器的功能。通過這個方法可以讓容器在文件系統中讀取以XML形式存在的BeanDefinition。
