一、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。