設計模式(二十九)----綜合應用-自定義Spring框架-Spring IOC相關介面分析

2023-03-27 21:01:13

1 BeanFactory解析

Spring中Bean的建立是典型的工廠模式,這一系列的Bean工廠,即IoC容器,為開發者管理物件之間的依賴關係提供了很多便利和基礎服務,在Spring中有許多IoC容器的實現供使用者選擇,其相互關係如下圖所示。

其中,BeanFactory作為最頂層的一個介面,定義了IoC容器的基本功能規範,BeanFactory有三個重要的子介面:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。但是從類圖中我們可以發現最終的預設實現類是DefaultListableBeanFactory,它實現了所有的介面。

那麼為何要定義這麼多層次的介面呢?

每個介面都有它的使用場合,主要是為了區分在Spring內部操作過程中物件的傳遞和轉化,對物件的資料存取所做的限制。例如,

  • ListableBeanFactory介面表示這些Bean可列表化。

  • HierarchicalBeanFactory表示這些Bean 是有繼承關係的,也就是每個 Bean 可能有父 Bean

  • AutowireCapableBeanFactory 介面定義Bean的自動裝配規則。

這三個介面共同定義了Bean的集合、Bean之間的關係及Bean行為。最基本的IoC容器介面是BeanFactory,來看一下它的原始碼:

public interface BeanFactory {
​
    String FACTORY_BEAN_PREFIX = "&";
​
    //根據bean的名稱獲取IOC容器中的的bean物件
    Object getBean(String name) throws BeansException;
    //根據bean的名稱獲取IOC容器中的的bean物件,並指定獲取到的bean物件的型別,這樣我們使用時就不需要進行型別強轉了
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    
    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
​
    //判斷容器中是否包含指定名稱的bean物件
    boolean containsBean(String name);
    //根據bean的名稱判斷是否是單例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    String[] getAliases(String name);
}

在BeanFactory裡只對IoC容器的基本行為做了定義,根本不關心你的Bean是如何定義及怎樣載入的。正如我們只關心能從工廠裡得到什麼產品,不關心工廠是怎麼生產這些產品的。

BeanFactory有一個很重要的子介面,就是ApplicationContext介面,該介面主要來規範容器中的bean物件是非延時載入,即在建立容器物件的時候就物件bean進行初始化,並儲存到一個容器中。

要知道工廠是如何產生物件的,我們需要看具體的IoC容器實現,Spring提供了許多IoC容器實現,比如:

  • ClasspathXmlApplicationContext : 根據類路徑載入xml組態檔,並建立IOC容器物件。

  • FileSystemXmlApplicationContext :根據系統路徑載入xml組態檔,並建立IOC容器物件。

  • AnnotationConfigApplicationContext :載入註解類設定,並建立IOC容器。

2 BeanDefinition解析

Spring IoC容器管理我們定義的各種Bean物件及其相互關係,而Bean物件在Spring實現中是以BeanDefinition來描述的,如下面組態檔

<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"></bean>
​
bean標籤還有很多屬性:
    scope、init-method、destory-method等。

其繼承體系如下圖所示。

3 BeanDefinitionReader解析

Bean的解析過程非常複雜,功能被分得很細,因為這裡需要被擴充套件的地方很多,必須保證足夠的靈活性,以應對可能的變化。Bean的解析主要就是對Spring組態檔的解析。這個解析過程主要通過BeanDefinitionReader來完成,看看Spring中BeanDefinitionReader的類結構圖,如下圖所示。

看看BeanDefinitionReader介面定義的功能來理解它具體的作用:

public interface BeanDefinitionReader {
​
    //獲取BeanDefinitionRegistry註冊器物件
    BeanDefinitionRegistry getRegistry();
​
    @Nullable
    ResourceLoader getResourceLoader();
​
    @Nullable
    ClassLoader getBeanClassLoader();
​
    BeanNameGenerator getBeanNameGenerator();
​
    /*
        下面的loadBeanDefinitions都是載入bean定義,從指定的資源中
    */
    int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
    int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
    int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
    int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}

4 BeanDefinitionRegistry解析

BeanDefinitionReader用來解析bean定義,並封裝BeanDefinition物件,而我們定義的組態檔中定義了很多bean標籤,所以就有一個問題,解析的BeanDefinition物件儲存到哪兒?答案就是BeanDefinition的註冊中心,而該註冊中心頂層介面就是BeanDefinitionRegistry。

public interface BeanDefinitionRegistry extends AliasRegistry {
​
    //往登入檔中註冊bean
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException;
​
    //從登入檔中刪除指定名稱的bean
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
​
    //獲取登入檔中指定名稱的bean
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    
    //判斷登入檔中是否已經註冊了指定名稱的bean
    boolean containsBeanDefinition(String beanName);
    
    //獲取登入檔中所有的bean的名稱
    String[] getBeanDefinitionNames();
    
    int getBeanDefinitionCount();
    boolean isBeanNameInUse(String beanName);
}

繼承結構圖如下:

從上面類圖可以看到BeanDefinitionRegistry介面的子實現類主要有以下幾個:

  • DefaultListableBeanFactory

    在該類中定義瞭如下程式碼,就是用來註冊bean

    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
  • SimpleBeanDefinitionRegistry

    在該類中定義瞭如下程式碼,就是用來註冊bean

    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);

5 建立容器

ClassPathXmlApplicationContext對Bean設定資源的載入是從refresh()方法開始的。refresh()方法是一個模板方法,規定了 IoC 容器的啟動流程,有些邏輯要交給其子類實現。它對 Bean 設定資源進行載入,ClassPathXmlApplicationContext通過呼叫其父類別AbstractApplicationContext的refresh()方法啟動整個IoC容器對Bean定義的載入過程。