Spring源碼解析一:IOC容器設計


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM