spring 源碼學習---IOC相關內容

一 概念

       IOC(Inversion of Control)控制反轉:所謂控制反轉,就是把原先我們代碼裏面需要實現的對象創 建、依賴的代碼,反轉給容器來幫忙實現。那麼必然的我們需要創建一個容器,同時需要一種描述來讓 容器知道需要創建的對象與對象的關係。這個描述最具體表現就是我們可配置的文件。 

     DI(Dependency Injection)依賴注入:就是指對象是被動接受依賴類而不是自己主動去找,換句話說 就是指對象不是從容器中查找它依賴的類,而是在容器實例化對象的時候主動將它依賴的類注入給它。 

    依賴查找(DependencyLookup)  : 主動根據 spring 提供的api查詢相應的bean.

二、 核心容器

       Spring Bean 的創建是典型的工廠模式,這一系列的 Bean 工廠,也即 IOC 容器爲開發者管理對象間的依賴關係提供了很多便利和基礎服務,在 Spring 中有許多的 IOC 容器的實現供用戶選擇和使用, 其相互關係如下:

                       

  2.1、 BeanFactory 

    BeanFactory 作爲最頂層的一個接口類,它定義了 IOC 容器的基本功能規範。

    BeanFactory 有三個子類:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。 但是從上圖中我們可以發現最終的默認實現類DefaultListableBeanFactory,他實現了所有的接 口。那爲何要定義這麼多層次的接口呢?查閱這些接口的源碼和說明發現,每個接口都有他使用的場合, 它主要是爲了區分在 Spring 內部在操作過程中對象的傳遞和轉化過程中,對象的數據訪問所做的限制。

  ListableBeanFactory: 接口表示這些 Bean 是可列表的,依賴查找 bean的集合。

  HierarchicalBeanFactory :有層次的bean,父容器類似雙親委派機制。表示的是這些Bean是有繼承關係的,也就是每個Bean有可能有父Bean。

  AutowireCapableBeanFactory:接口定義 Bean 的自動裝配規則。

  DefaultListableBeanFactory : 默認實現的

DefaultListableBeanFactory 實現的設計模式有:
抽象工廠(BeanFactory 接口實現)
組合模式(組合 AutowireCandidateResolver 等實例)
單例模式(Bean Scope)
原型模式(Bean Scope)
模板模式(模板方法定義:AbstractBeanFactory)
適配器模式(適配 BeanDefinitionRegistry 接口)
策略模式(Bean 實例化)
代理模式(ObjectProvider 代理依賴查找)

2.2、 ApplicationContext

    如果說BeanFactory 是容器中的屌絲,ApplicationContext 應該算容器中的高帥富。 ApplicationContext 是具備應用特性的BeanFactory 超集。

    ApplicationContext 是 Spring 提供的一個高級的IOC 容器, ApplicationContext 除了IoC 容器角色,還有提供:

  •  面向切面(AOP)
  •  配置元信息(Configuration Metadata)
  •  資源管理(Resources)
  • 事件(Events)
  • 國際化(i18n)
  • 註解(Annotations)
  • Environment 抽象(Environment Abstraction)

2.3、 具體實現

    BeanFactory 裏只對 IOC 容器的基本行爲作了定義,根本不關心你的 Bean 是如何定義怎樣加載的。 正如我們只關心工廠裏得到什麼的產品對象,至於工廠是怎麼生產這些對象的,這個基本的接口不關心。

public interface BeanFactory {

	//對FactoryBean的轉義定義,因爲如果使用bean的名字檢索FactoryBean得到的對象是工廠生成的對象,
	//如果需要得到工廠本身,需要轉義
	String FACTORY_BEAN_PREFIX = "&";

	//根據bean的名字,獲取在IOC容器中得到bean實例
	Object getBean(String name) throws BeansException;
	
	//根據bean的名字和Class類型來得到bean實例,增加了類型安全驗證機制。
	<T> T getBean(String name, @Nullable 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;

	//提供對bean的檢索,看看是否在IOC容器有這個名字的bean
	boolean containsBean(String name);

	//根據bean名字得到bean實例,並同時判斷這個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, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	//得到bean實例的Class類型
	@Nullable
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	//得到bean的別名,如果根據別名檢索,那麼其原名也會被檢索出來
	String[] getAliases(String name);

}

      而要知道工廠是如何產生對象的,我們需要看具體的 IOC 容器實現,Spring 提供了許多 IOC 容器 的實現。比如 XmlBeanFactory,ClasspathXmlApplicationContext 、AnnotationConfigApplicationContext等。其中 XmlBeanFactory 就 是針對最基本的 IOC 容器的實現,這個 IOC 容器可以讀取 XML 文件定義的 BeanDefinition(XML 文件中對 bean 的描述),如果說 XmlBeanFactory 是容器中的屌絲,ClasspathXmlApplicationContext 應該算容器中 的高帥富.。

2.4 、具體代碼

1、BeanFactory 作爲 IoC 容器示例

/**
 * {@link BeanFactory} 作爲 IoC 容器示例
 *
 * @author
 * @since
 */
public class BeanFactoryAsIoCContainerDemo {

    public static void main(String[] args) {
        // 創建 BeanFactory 容器
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        // XML 配置文件 ClassPath 路徑
        String location = "classpath:/META-INF/dependency-lookup-context.xml";
        // 加載配置
        int beanDefinitionsCount = reader.loadBeanDefinitions(location);
        System.out.println("Bean 定義加載的數量:" + beanDefinitionsCount);
        // 依賴查找集合對象
        lookupCollectionByType(beanFactory);
    }

    private static void lookupCollectionByType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
            System.out.println("查找到的所有的 User 集合對象:" + users);
        }
    }

}

 2、ApplicationContext 作爲 IoC 容器示例

/**
 * 註解能力 {@link ApplicationContext} 作爲 IoC 容器示例
 *
 * @author
 * @since
 */
@Configuration
public class AnnotationApplicationContextAsIoCContainerDemo {

    public static void main(String[] args) {
        // 創建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 將當前類 AnnotationApplicationContextAsIoCContainerDemo 作爲配置類(Configuration Class)
        applicationContext.register(AnnotationApplicationContextAsIoCContainerDemo.class);
        // 啓動應用上下文
        applicationContext.refresh();
        // 依賴查找集合對象
        lookupCollectionByType(applicationContext);
        // 關閉應用上下文
        applicationContext.close();

    }

    /**
     * 通過 Java 註解的方式,定義了一個 Bean
     */
    @Bean
    public User user() {
        User user = new User();
        user.setId(1L);
        user.setName("坤仔");
        return user;
    }

    private static void lookupCollectionByType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
            System.out.println("查找到的所有的 User 集合對象:" + users);
        }
    }

}

其中  下面兩段代碼相等

  AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
       applicationContext.register(AnnotationApplicationContextAsIoCContainerDemo.class);
       applicationContext.refresh();
AnnotationConfigApplicationContext applicationContext1 = 
new AnnotationConfigApplicationContext(AnnotationApplicationContextAsIoCContainerDemo.class);

器底層實現:

public AnnotationConfigApplicationContext(Class... componentClasses) {
    this();
    this.register(componentClasses);
    this.refresh();
}

2.5、容器生命週期

 1 啓動 ——> 運行   ——> 停止

// 創建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext =
 new AnnotationConfigApplicationContext();
        // 將當前類 AnnotationApplicationContextAsIoCContainerDemo 作爲配置類(Configuration Class)
        applicationContext.register(AnnotationApplicationContextAsIoCContainerDemo.class);
        // 啓動應用上下文
        applicationContext.refresh();
       
        // 關閉應用上下文
        applicationContext.close();

三 、依賴查找 DependencyLookup

  應用程序裏面還是要調用容器的bean查找接口查找bean實例。 缺點:還是有侵入性,性能低。

  

 注意: 延遲查找的意思: 延遲是指非一次性獲得 

代碼示例:

/**
 * 依賴查找示例
 * 1. 通過名稱的方式來查找
 *
 * @author
 * @since
 */
public class DependencyLookupDemo {

    public static void main(String[] args) {
        // 配置 XML 配置文件
        // 啓動 Spring 應用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
        // 按照名字查找
        lookupByName(beanFactory);
        // 按照類型查找
        lookupByType(beanFactory);
        // 按照類型查找集合對象
        lookupCollectionByType(beanFactory);
        // 通過註解查找對象
        lookupByAnnotationType(beanFactory);
        // 使用 ObjectFactory實現延遲查找
        lookupInLazy(beanFactory);
    }

    //按照名字查找
    private static void lookupByName(BeanFactory beanFactory) {
        User user = (User)beanFactory.getBean("user");
        System.out.println("按照名字實時查找:" + user);
    }
    //按照類型查找
    private static void lookupByType(BeanFactory beanFactory) {
        User user = beanFactory.getBean(User.class);
        System.out.println("按照類型實時查找:" + user);
    }

    /**
     * 按照類型查找集合對象
     * 根據 User能查詢出來兩個bean
     * 使用 ListableBeanFactory
     * @param beanFactory
     */
    private static void lookupCollectionByType(BeanFactory beanFactory) {
      if(beanFactory instanceof ListableBeanFactory ){
          ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
          Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
          System.out.println("查找到的所有的 User 集合對象:" + users);
      }
    }

    /**
     * 根據註解查找  查找標註註解爲@Super 的bean
     * @param beanFactory
     */
    private static void lookupByAnnotationType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = (Map) listableBeanFactory.getBeansWithAnnotation(Super.class);
            System.out.println("查找標註 @Super 所有的 User 集合對象:" + users);
        }
    }

    /**
     * 使用 ObjectFactory實現延遲查找
     * @param beanFactory
     */
    private static void lookupInLazy(BeanFactory beanFactory) {
        ObjectFactory<User> objectFactory = (ObjectFactory<User>) beanFactory.getBean("objectFactory");
        User user = objectFactory.getObject();
        System.out.println("延遲查找:" + user);
    }


}

3.1 單一類型查找 BeanFactory

單一類型依賴查找接口-BeanFactory  
    • 根據Bean 名稱查找 
        • getBean(String) 
        • Spring 2.5 覆蓋默認參數:getBean(String,Object...) 
    • 根據Bean 類型查找 
        • Bean 實時查找 
        • Spring 3.0 getBean(Class) 
        • Spring 4.1 覆蓋默認參數:getBean(Class,Object...) 
    • Spring 5.1  Bean 延遲查找 
        • getBeanProvider(Class) 
        • getBeanProvider(ResolvableType) 
    • 根據Bean 名稱+ 類型查找:getBean(String,Class)

    代碼示例 其他在 上面已經放入代碼了。

3.2、集合類型依賴查找接口-ListableBeanFactory

集合類型依賴查找接口-ListableBeanFactory 
    • 根據Bean 類型查找 
        • 獲取同類型Bean 名稱列表 
            • getBeanNamesForType(Class) 
            • Spring 4.2 getBeanNamesForType(ResolvableType) 
        • 獲取同類型Bean 實例列表 
            • getBeansOfType(Class) 以及重載方法 
    • 通過註解類型查找 
        • Spring 3.0 獲取標註類型Bean 名稱列表 
            • getBeanNamesForAnnotation(Class<? extends Annotation>) 
        • Spring 3.0 獲取標註類型Bean 實例列表 
            • getBeansWithAnnotation(Class<? extends Annotation>) 
        • Spring 3.0 獲取指定名稱+ 標註類型Bean 實例 
            • findAnnotationOnBean(String,Class<? extends Annotation>)

    接口方法:

     獲取同類型Bean 名稱列表 :getBeanNamesForType(Class)  根據類型查找beanName,其實是從BeanDefinition 查找類型去匹配,BeanDefinition特點bean的定義原信息並不是bean已經被初始化了。

    獲取同類型Bean 實例列表 :getBeansOfType(Class) ,可能會導致bean提前初始化,導致信息不全出現問題。

   所以一般在判斷時候首先要根據名字去判斷,在根據類型去判斷。

@Configuration
public class ObjectProviderDemo {

    public static void main(String[] args) {
        // 創建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 註冊 BeanDefinition 將當前類 ObjectProviderDemo 作爲配置類(Configuration Class)
        applicationContext.register(ObjectProviderDemo.class);
        //啓動 容器上下文
        applicationContext.refresh();
        // 按照類型查找集合對象
        lookupCollectionByType(applicationContext);
        //關閉
        applicationContext.close();

    }

    private static void lookupCollectionByType(AnnotationConfigApplicationContext applicationContext) {
        if(applicationContext instanceof ListableBeanFactory){
             ListableBeanFactory listableBeanFactory = applicationContext;
            String[] resultString = listableBeanFactory.getBeanNamesForType(String.class);
            Map<String, String> result = listableBeanFactory.getBeansOfType(String.class);
            Arrays.asList(resultString).forEach(System.out::println);
            System.out.println("查找到的所有的 String 集合對象:" + result);
        }
    }


    @Bean
    public String helloWorld() { // 方法名就是 Bean 名稱 = "helloWorld"
        return "Hello,World";
    }

    @Bean
    public String message() {
        return "Message";
    }
}

3.3、層次性依賴查找接口-HierarchicalBeanFactory

   HierarchicalBeanFactory接口提供 獲取父容器的方法, 這個機制類似與雙親委派機制。只有繼承他的都要層次結構。

   子接口:ConfigurableBeanFactory:可配置的接口。

                  ConfigurableListableBeanFactory接口 是使用組合模塊。他具有 集合、層次、自動注入的功能。

                  AbstractBeanFactory中containsBean也有對父容器的檢查,遞歸的實現。

HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListableBeanFactory
public interface ConfigurableListableBeanFactory
      extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory 

實例代碼

/**
 * 層次性依賴查找示例
 * Created by dukun on 2020/7/2.
 */
@Configuration
public class HierarchicalDependencyLookupDemo {

    public static void main(String[] args) {
        // 創建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 將當前類 ObjectProviderDemo 作爲配置類(Configuration Class)
        applicationContext.register(ObjectProviderDemo.class);
        // 1. 獲取 HierarchicalBeanFactory <- ConfigurableBeanFactory <- ConfigurableListableBeanFactory
        ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory();
        // 2. 設置 Parent BeanFactory
        HierarchicalBeanFactory parentBeanFactory = createParentBeanFactory();
        beanFactory.setParentBeanFactory(parentBeanFactory);

        //判斷本地容器是否包含 bean
        displayContainsLocalBean(beanFactory, "user");//這個應該是不包含
        displayContainsLocalBean(parentBeanFactory, "user");//這個本地容器包含
        System.out.println("==============================================");
        displayContainsBean(beanFactory, "user");

    }
    //判斷本地容器是否包含 bean
    private static void displayContainsLocalBean(HierarchicalBeanFactory beanFactory, String beanName) {
        System.out.printf("當前 BeanFactory[%s] 是否包含 Local Bean[name : %s] : %s\n", beanFactory, beanName,
                beanFactory.containsLocalBean(beanName));
    }
    
    //判斷是否bean
    private static void displayContainsBean(HierarchicalBeanFactory beanFactory, String beanName) {
        System.out.printf("當前 BeanFactory[%s] 是否包含 Bean[name : %s] : %s\n", beanFactory, beanName,
                containsBean(beanFactory, beanName));
    }
    //遞歸判斷父容器或者本地容器是否包含bean
    private static boolean containsBean(HierarchicalBeanFactory beanFactory, String beanName) {
        BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
        if (parentBeanFactory instanceof HierarchicalBeanFactory) {
            HierarchicalBeanFactory parentHierarchicalBeanFactory = HierarchicalBeanFactory.class.cast(parentBeanFactory);
            if (containsBean(parentHierarchicalBeanFactory, beanName)) {
                return true;
            }
        }
        return beanFactory.containsLocalBean(beanName);
    }


    //創建父容器
    private static HierarchicalBeanFactory createParentBeanFactory() {
        // 創建 BeanFactory 容器
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        // XML 配置文件 ClassPath 路徑
        String location = "classpath:/META-INF/dependency-lookup-context.xml";
        // 加載配置
        reader.loadBeanDefinitions(location);
        return beanFactory;
    }
}

   問題基本都是單一查找,依賴查找可以在LB這種場景應用,但這個分層查找有啥用?主要複用bean

         比如 Spring MVC 中,Biz 組件放在 Root ApplicationContext,而 Web 組件放在 DispatcherServlet 的 ApplicationContext,後者是前者的子 ApplicationContext,所以,子 ApplicationContext 可以讀取父 ApplicationContext。

        容器的層次關係主要的目的是實現 Bean 複用,假設一個應用存在一個 Root ApplicationContext,內部的 Bean 來自於一個 jar 包,那麼,這個jar 包可能被兩個不同的 Servlet 應用使用,這時,ServletContext A 和 ServletContext B 同時複用了這個 parent ApplicationContext,而它自己也有 ApplicationContext,這也是 Spring Web MVC 所涉及的應用上下文架構。

3.4、Bean 延遲依賴查找接口 

延遲是指非一次性獲得 

• org.springframework.beans.factory.ObjectFactory
• org.springframework.beans.factory.ObjectProvider 
    • Spring 5 對Java 8 特性擴展 
        • 函數式接口 
            • getIfAvailable(Supplier) 
            • ifAvailable(Consumer) 
        • Stream 擴展-stream()

getBeanProvider方法 爲ObjectProvider 中的  其又繼承 ObjectFactory。

  延遲查找(延遲是指非一次性獲得 :主要用於暫時性地獲取某個 Bean Holder 對象,如果過早的加載,可能會引起未知的狀態,比如,當 A bean 依賴 B bean 時,如果過早地初始化 A,那麼 B bean 裏面的狀態可能是中間狀態,這樣容易導致一些錯誤。

  ObjectProvider 對象獲取時,並沒有獲取實際 Bean 的對象,而是在 getObject() 或其他獲取方法時才獲取。

/**
 * 通過 {@link ObjectProvider} 進行依賴查找
 * Created by dukun on 2020/7/2.
 */
@Configuration
public class ObjectProviderDemo {

    public static void main(String[] args) {
        // 創建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 註冊 BeanDefinition 將當前類 ObjectProviderDemo 作爲配置類(Configuration Class)
        applicationContext.register(ObjectProviderDemo.class);
        //啓動 容器上下文
        applicationContext.refresh();
        //單一類型的延遲查找
        lookupByObjectProvider(applicationContext);
        //關閉
        applicationContext.close();

    }

    //ObjectProvider 繼承 ObjectFactory 
    private static void lookupByObjectProvider(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
        System.out.println(objectProvider.getObject());
    }

   private static void lookupByStreamOps(AnnotationConfigApplicationContext applicationContext) {
        ObjectProvider<String> objectProvider = applicationContext.getBeanProvider(String.class);
        objectProvider.stream().forEach(System.out::println);
    }

    @Bean
    public String helloWorld() { // 方法名就是 Bean 名稱 = "helloWorld"
        return "Hello,World";
    }
}

3.6 依賴查找中的經典異常

BeansException 子類型

異常類型   觸發條件(舉例)  場景舉例
NoSuchBeanDefinitionException  當查找Bean 不存在於IoC 容器時  BeanFactory#getBean ObjectFactory#getObject 
NoUniqueBeanDefinitionException
 
類型依賴查找時,IoC 容器存在多 個Bean 實例 BeanFactory#getBean(Class) 
BeanInstantiationException 當Bean 所對應的類型非具體類時 BeanFactory#getBean
BeanCreationException 當Bean 初始化過程中  Bean 初始化方法執行異常 時 
BeanDefinitionStoreException 當BeanDefinition 配置元信息非法 時 XML 配置資源無法打開時

安全依賴查找(這裏安全是指是否會報異常)

內建可查找的依賴

• AbstractApplicationContext 內建可查找的依賴

• 註解驅動Spring 應用上下文內建可查找的依賴(部分)

 註解驅動Spring 應用上下文內建可查找的依賴(續)

四、Spring IoC 依賴注入

直接在容器啓動時通過構造器,getter setter等形式注入依賴。

優點:性能高,侵入小

兩者區別:區別在於依賴的對象是否爲主動獲取,是的話,就是依賴查找,否則就是依賴注入,由框架綁定完成。

 

 

  

 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章