天天看點

Spring Ioc 容器的設計

文章目錄

  • ​​1.Spring IOC容器的設計​​
  • ​​2.BeanFactory和ApplicationContext的差別​​
  • ​​3.BeanFactory容器的設計原理​​
  • ​​4.BeanFactory的詳細介紹​​
  • ​​5.ApplicationContext容器的設計原理​​
  • ​​6.ApplicationContext的詳細介紹​​
  • ​​7.ApplicationContext容器擴充功能詳解介紹​​

1.Spring IOC容器的設計

  • 實作BeanFactory接口的簡單容器
  • 實作ApplicationContext接口的進階容器
Spring Ioc 容器的設計

ApplicationContext是BeanFactory的子接口。BeanFactory是Spring IoC容器的最底層接口, 隻提供了IOC容器的基本功能, 給具體的IOC 容器的實作提供了規範, 主要負責配置, 生産和管理Bean。内部定義了對單個bean的擷取,對bean的作用域判斷,擷取bean類型,擷取bean别名等功能。

而ApplicationContext繼承了MessageSource、ListableBeanFactory、ResourceLoader、ApplicationEventPublisher等接口。是進階的BeanFactory。進階容器

上面兩個重要的類都是接口,既然是接口那總得有具體的實作類。DefaultListableBeanFactory. 實作了包含所有Spring IOC 容器的基本功能。真正可以作為一個可以獨立使用的IOC容器還是DefaultListableBeanFactory, 而不是BeanFactory。所有DefaultListenerBeanFactory是Ioc容器的始祖。

2.BeanFactory和ApplicationContext的差別

BeanFactory和ApplicationContext是Spring IOC容器的兩大核心接口。ApplicationContext是BeanFactory的子接口。

①、提供的功能不同:

BeanFactory提供了Bean的定義,讀取bean配置文檔,管理bean的加載、執行個體化,控制bean的生命周期,維護bean之間的依賴關系等。

public interface ApplicationContext extends 
                            EnvironmentCapable,
                            ListableBeanFactory,
                            HierarchicalBeanFactory,
                            MessageSource,
                            ApplicationEventPublisher,
                            ResourcePatternResolver {

}
      
  • 支援國際化(MessageSource)
  • 統一的資源檔案通路方式(ResourcePatternResolver)
  • 提供在監聽器中注冊bean的事件(ApplicationEventPublisher)
  • 同時加載多個配置檔案
  • 載入多個(有繼承關系)上下文 ,使得每一個上下文都專注于一個特定的層次,比如應用的web層(HierarchicalBeanFactory)

②、 啟動時的狀态不同:

BeanFactroy采用的是延遲加載形式來注入Bean的,使用的時候對Bean執行個體化, 不存在Spring的配置問題, 如果一個Bean的屬性未注入, BeanFactory加載後, 直到第一次調用getBean()的時候才會抛出異常。

ApplicationContext:啟動的時候, 創所有的Bean, 這樣, 容器啟動的時候, 發現配置錯誤的話, 有利于檢查依賴是否注入。相對于基本的BeanFactory,ApplicationContext 唯一的不足是占用記憶體空間。當應用程式配置Bean較多時,程式啟動較慢。

③、BeanFactory通常以程式設計的方式被建立,ApplicationContext還能以聲明的方式建立,如使用ContextLoader。

④、BeanFactory和ApplicationContext都支援BeanPostProcessor、BeanFactoryPostProcessor的使用,但兩者之間的差別是:BeanFactory需要手動注冊,而ApplicationContext則是自動注冊。

3.BeanFactory容器的設計原理

Spring Ioc 容器的設計
  • HierarchicalBeanFactory:提供父容器的通路功能,它内部定義了兩個方法。
  • ListableBeanFactory:提供了列出工廠中所有的Bean的方法 定義了容器内Bean的枚舉功能(枚舉出來的Bean不會包含父容器)。
  • AutowireCapableBeanFactory:在BeanFactory基礎上實作對已存在執行個體的管理,主要定義了內建其它架構的功能。一般應用開發者不會使用這個接口,是以像ApplicationContext這樣的外觀實作類不會實作這個接口,如果真想用可以通過ApplicationContext的getAutowireCapableBeanFactory接口擷取。
  • ConfigurableBeanFactory:定義了BeanFactory的配置功能。
  • ConfigurableListableBeanFactory:繼承了上述的所有接口,增加了其他功能:比如類加載器、類型轉化、屬性編。
  • BeanPostProcessor、作用域、bean定義、處理bean依賴關系、bean如何銷毀等功能。
  • DefaultListableBeanFactory:實作上述BeanFactory接口中所有功能。它還可以注冊BeanDefinition。
  • XmlBeanFactory :在Spring3.1之前使用,後面被标記為Deprecated,繼承自DefaultListableBeanFactory,增加了對Xml檔案解析的支援。

XmlBeanFactory是BeanFactory體系中的最底層的實作類。XmlBeanFactory在父類的基礎上增加了對XML檔案解析的支援,也就是說它是一個可以讀取XML檔案方式定義BeanDefinition的IOC容器。

BeanDefination: 它是對 IOC容器中管理的對象依賴關系的資料抽象。控制反轉功能都是圍繞對這個BeanDefinition的處理來完成的。

BeanDefinition在Spring中是用來描述Bean對象的,它本身并不是一個Bean執行個體,而是包含了Bean執行個體的所有資訊,比如類名、屬性值、構造器參數、scope、依賴的bean、是否是單例類、是否是懶加載以及其它資訊。其實就是将Bean執行個體定義的資訊存儲到這個BeanDefinition相應的屬性中,後面Bean對象的建立是根據BeanDefinition中描述的資訊來建立的,例如拿到這個BeanDefinition後,可以根據裡面的類名、構造函數、構造函數參數,使用反射進行對象建立。也就是說 IOC容器可以有多個BeanDefinition,并且一個BeanDefinition對象對應一個标簽中的資訊。

當然BeanDefinition的最終目的不隻是用來存儲Bean執行個體的所有資訊,而是為了可以友善的進行修改屬性值和其他元資訊,比如通過BeanFactoryPostProcessor進行修改一些資訊,然後在建立Bean對象的時候就可以結合原始資訊和修改後的資訊建立對象了。

XmlBeanFactory:

@Deprecated
public class XmlBeanFactory extends DefaultListableBeanFactory {

    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
    
    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }   
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        this.reader.loadBeanDefinitions(resource);
    }
}
      

初始化了一個 XmlBeanDefinitionReader對象, 目的是讀取xml檔案, 将Bean的xml配置檔案轉化為多個BeanDefinition工具類, 一個BeanDefination對象對應一個标簽的資訊。

執行loadBeanDefinition()方法: 加載BeanDefination的操作,

4.BeanFactory的詳細介紹

Spring Ioc 容器的設計

5個擷取執行個體的方法(getBean的重載方法);2個擷取Bean的提供者;4個判斷的方法(判斷是否存在,是否為單例、原型,名稱類型是否比對);2個擷取類型的方法和1個擷取别名的方法。

public interface BeanFactory {

    //使用者使用容器時,可以使用轉義符“&”來得到FactoryBean本身
    String FACTORY_BEAN_PREFIX = "&";

    //擷取Bean
    Object getBean(String name) throws BeansException;
     T getBean(String name, Class requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
     T getBean(Class requiredType) throws BeansException;
     T getBean(Class requiredType, Object... args) throws BeansException;

    //擷取bean的提供者(對象工廠)
     ObjectProvider getBeanProvider(Class requiredType);
     ObjectProvider getBeanProvider(ResolvableType requiredType);

    //判斷是否包含指定名字的bean
    boolean containsBean(String name); 
    //擷取指定名字的Bean是否是Singleton類型的Bean,對于Singleton屬性,使用者可以在BeanDefinition中指定
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    //擷取指定名字的Bean是否是Prototype類型的,與Singleton屬性一樣也可以在BeanDefinition中指定
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    //指定名字的bean是否和指定的類型比對
    boolean isTypeMatch(String name, ResolvableType typeToMatch);
    boolean isTypeMatch(String name, Class typeToMatch) throws NoSuchBeanDefinitionException;

    //擷取指定名字的Bean的Class類型
    Class getType(String name) throws NoSuchBeanDefinitionException; 

    //擷取指定名字的Bean的所有别名,這些别名是使用者在BeanDefinition中定義的
    String[] getAliases(String name); 
}
      
  1. getBean部分(重要):該方法表示擷取bean執行個體

    ①、根據名字擷取bean:getBean(String name)

    ②、根據類型擷取bean:getBean(Class requiredType)

    ③、根據名字和類型擷取bean(推薦):getBean(String name, Class requiredType)

    ④、根據名稱、類型和給定的構造函數參數或者工廠方法參數構造對象擷取bean

  2. getBeanProvider部分:該方法表示擷取bean的提供者(對象工廠)
  3. containsBean(String name):通過名字判斷是否包含指定bean的定義 。

    isSingleton(String name) isPrototype(String name):判斷是單例和原型(多例)的方法。

    isTypeMatch:判斷給定bean的名字是否和類型比對 。

    getType(String name):根據bean的名字來擷取其類型的方法 (按 Java 類型比對的方式 )。

    getAliases(String name):根據bean的名字來擷取其别名的方法。

  4. ResolvableType參數介紹

    ResolvableType是對Java java.lang.reflect.Type的封裝,并且提供了一些通路該類型的其他資訊的方法(例如父類, 泛型參數,該類)。從成員變量、方法參數、方法傳回類型、類來建構ResolvableType的執行個體。

5.ApplicationContext容器的設計原理

Spring Ioc 容器的設計

ClassPathXmlApplicationContext:

ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
      
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
        this(new String[] {configLocation}, true, null);
    }
      

configLocation表示的是Spring配置檔案的路徑

public ClassPathXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
            throws BeansException {
        // 1.初始化父類
        super(parent);
        // 2.設定本地的配置資訊
        setConfigLocations(configLocations);
        // 3.完成Spring IOC容器的初始化
        if (refresh) {
            refresh();
        }
    }
      

首先初始化了父類,就是一直到父類AbstractApplicationContext中,将ApplicationContext的環境屬性設定給本類的環境屬性,包括一些profile,系統屬性等。

然後設定本地的配置檔案資訊,這裡調用其父類AbstractRefreshableConfigApplicationContext 的 setConfigLocations 方法,該方法主要處理ClassPathXmlApplicationContext傳入的字元串中的占位符,即解析給定的路徑數組(這裡就一個),setConfigLocations 方法源碼如下:

public void setConfigLocations(@Nullable 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++) {
                //循環取出每一個path參數,在此處就一個applicationContext.xml
                this.configLocations[i] = resolvePath(locations[i]).trim();
            }
        }
        else {
            this.configLocations = null;
        }
    }
      

setConfigLocations方法除了處理ClassPathXmlApplicationContext傳入的字元串中的占位符之外,其實還有一個作用:建立環境對象ConfigurableEnvironment。

refresh():

進階容器的所有功能(包括 IoC)

//AbstractApplicationContext.refresh()方法
public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            //重新整理上下文環境
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            //這裡是在子類中啟動 refreshBeanFactory() 的地方,獲得新的BeanFactory,解析XML、Java類,并加載BeanDefinition
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            //準備bean工廠,以便在此上下文中使用
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                //設定 beanFactory 的後置處理
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                //調用 BeanFactory 的後處理器,這些處理器是在Bean 定義中向容器注冊的
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                //注冊Bean的後處理器,在Bean建立過程中調用
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                //對上下文中的消息源進行初始化
                initMessageSource();

                // Initialize event multicaster for this context.
                //初始化上下文中的事件機制
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                //初始化其他特殊的Bean
                onRefresh();

                // Check for listener beans and register them.
                //檢查監聽Bean并且将這些監聽Bean向容器注冊
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                //執行個體化所有的(non-lazy-init)單件
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                //釋出容器事件,結束Refresh過程
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                //重置Spring公共的緩存
                resetCommonCaches();
            }
        }
    }
      
  • prepareRefresh() :為重新整理準備上下文,主要設定狀态量(是否關閉,是否激活),記錄啟動時間,初始化屬性資源占位符、校驗必填屬性是否配置及初始化用于存儲早期應用事件的容器。
  • obtainFreshBeanFactory():主要用于擷取一個新的BeanFactory,如果BeanFactory已存在,則将其銷毀并重建,預設重建的BeanFactory為AbstractRefreshableApplicationContext;此外此方法委托其子類從XML中或基于注解的類中加載BeanDefinition。
  • prepareBeanFactory():配置BeanFactory使其具有一個上下文的标準特征,如上下文的類加載器、後處理程式(post-processors,如設定如總感覺接口)。
  • postprocessBeanFactory():在應用上下文内部的BeanFactory初始化結束後對其進行修改,在所有的BeanDefinition已被加載但還沒有執行個體化bean, 此刻可以注冊一些特殊的BeanPostFactory,如web應用會注冊ServletContextAwareProcessor等。
  • invokeBeanFactoryPostProcessors():調用注冊在上下文中的BeanFactoryPostProcessor,如果有順序則按順序調用,并且一定再單列對象執行個體化之前調用。
  • registerBeanPostProcessors():執行個體化并注冊BeanPostProcessor,如果有顯式的順序則按照順序調用一定在所有bean執行個體化之前調用。
  • initMessageSource():初始化MessageSource,如果目前上下文沒有定義則使用其父類的,如果BeanFactory中不能找到名稱為messageSource中的bean, 則預設使用DelegatingMessageSource。
  • initApplicationEventMulticaster():初始化ApplicationEventMulticaster,如果上下文沒有定義則預設使用 - SimpleApplicationEventMulticaster,此類主要用于廣播ApplicationEvent。
  • onRefresh() :在一些特定的上下文子類中初始化特定的bean,如在Webapp的上下文中初始化主題資源。
  • registerListeners():添加實作了ApplicationListener的bean作為監聽器,它不影響非bean的監聽器;還會使用多點傳播器釋出早期的ApplicationEvent。
  • finishBeanFactoryInitialization():執行個體化所有非延遲加載的單例,完成BeanFactory的初始化工作。
  • finishRefresh():完成上下文的重新整理工作,調用LifecycleProcessor的onFresh()及釋出的ContextRefreshEvent事件。
  • resetCommonCaches():重置Spring公共的緩存,如:ReflectionUtils、ResolvableType、CachedIntrospectionResults的緩存CachedIntrospectionResults的緩存。

14個方法

6.ApplicationContext的詳細介紹

Spring Ioc 容器的設計

(1)、ClassPathXmlApplicationContext:從系統類路徑classpath下加載一個或多個xml配置檔案,找到并裝載完成ApplicationContext的執行個體化工作。例如:

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
      

(2)、FileSystemXmlApplicationContext:從系統磁盤下加載一個或多個xml配置檔案(必須有通路權限)。也就是讀取系統磁盤指定路徑的xml檔案。例如:

ApplicationContext ac = new FileSystemXmlApplicationContext("c:/applicationContext.xml");
      

它與ClassPathXmlApplicationContext的差別在于讀取Spring配置檔案的方式,FileSystemXmlApplicationContext不在從類路徑下讀取配置檔案,而是通過制定參數從系統磁盤讀取,前提是有通路權限。

(3)、XmlWebApplicationContext:從web應用下加載一個或多個xml配置檔案,适用于web應用的xml配置方式。

在Java項目中提供ClassPathXmlApplicationContext類手工執行個體化ApplicationContext容器通常是不二之選,但是對于Web項目就不行了,Web項目的啟動是由相應的Web伺服器負責的,是以,在Web項目中ApplicationContext容器的執行個體化工作最好交由Web伺服器來完成。Spring為此提供了以下兩種方式:

  • org.springframework.web.context.ContextLoaderListener
  • org.springframework.web.context.ContexLoaderServlet(此方法目前以廢棄)
contextConfigLocation

        

            classpath:applicationContext.xml
        

    
    
    
        org.springframework.web.context.ContextLoaderListener
    
      
@WebServlet("/MyServlet")
public class MyServlet {

        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            //建立XmlWebApplicationContext對象,但這時并沒有初始化容器
            XmlWebApplicationContext context = new XmlWebApplicationContext();
            // 指定配置檔案路徑
            context.setConfigLocation("application.xml");
            // 需要指定ServletContext對象
            context.setServletContext(request.getServletContext());
            // 初始化容器
            context.refresh();
            //擷取執行個體
            Additive additive = (Additive) context.getBean("additive");
            additive.addAdditive();

        }
}
      

(4)、AnnotationConfigApplicationContext:從Java注解的配置類中加載Spring的ApplicationContext容器。使用注解避免使用application.xml進行配置。相比XML配置,更加便捷。

@Configuration
public class AppConfig {

    @Bean(name = "orangeJuice")
    public OrangeJuice orangeJuice(){
        OrangeJuice orangeJuice = new OrangeJuice();
        return orangeJuice;
    }

    @Bean(name = "additive")
    public Additive additive(){
        Additive additive = new Additive();
        return additive;
    }
}
      
contextClass

        

            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        

    
    
    
        
contextConfigLocation

        
com.thr.AppConfig

    
    
    
        org.springframework.web.context.ContextLoaderListener
    
      

7.ApplicationContext容器擴充功能詳解介紹