Spring IoC容器與應用上下文的設計與實現

一、前言

  寫這篇博文的主要目的如下:

  • 通過相關類和接口分析IoC容器到底長什麼樣。
  • 闡述筆者對Spring上下文和容器的理解。
  • 介紹重要的類輔助理解SpringBoot的啟動流程。

 

二、Spring IoC容器的設計

  看看下面這張圖(摘自《Spring技術內幕》),IoC容器的設計分為兩條線,

  1.   BeanFactory ==> HierarchicalBeanFactory ==>ConfigurableBeanFactory ,這條線可以理解成IoC容器的設計路線。
  2.   BeanFactory ==> ListableBeanFactory ==> ApplicationContext ==> ConfigurableApplicationContext ,這條可以成為Spring應用上下文的設計路線。

  為什麼這樣要分兩條線呢,主要是將容器和上下文區分開來。因為在在Spring項目中,上下文對容器不僅是擴展的關係,更重要的是持有的關係,上下文是以屬性的形式持有了容器,開發者可以通過上下文對象獲取到容器。筆者十分傾向於將二者分開來理解。當然也可以將應用上下文理解成容器的高級表現形式。

 

2.1,IoC容器的設計線路

  BeanFactory定義了IoC容器的基本規範,包括getBean()按類型和按名稱的獲取Bean的方法。

   

  HierarchicalBeanFactory 在BeanFactory的基礎上增加了getParentBeanFactory()方法,使BeanFactory具備了雙親IoC容器管理的功能。

  ConfigurableBeanFactory接口提供了配置BeanFactory的各種方法。比如setParentBeanFactory()方法,配置上面提到的雙親IoC容器,addBeanPostProcessor()方法,配置Bean後置處理器等。

  到這裏先埋個包袱:到ConfigurableBeanFactory接口為止,IoC容器還沒有具備作為“容器”最基本的功能,那就是能裝東西。

 

2.2、應用上下文設計路線

  上面說了應用上下文是IoC容器的高級表現形式,ListableBeanFactory具備了操作BeanDefinition 的能力,比如getBeanDefinitionCount()方法,可以獲取Bean的總數等。

  ApplicationContext 類那就厲害了,如下代碼所示,實現了一大堆接口

1 public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
2         MessageSource, ApplicationEventPublisher, ResourcePatternResolver
  • MessageSource,支持不同的信息源。具備支持國際化的實現,為開發多語言版本的應用提供服務。
  • ResourcePatternResolver,訪問數據源。具備了從不同地方得到Bean定義資源的能力,比如:xml,java config,註解等等。
  • ApplicationEventPublisher,發布事件。使應用上下文具備了事件機制。事件機製為Bean聲明周期的管理提供了便利。

  WebApplicationContext擴展了對web應用的支持。

  ConfigurableApplicationContext就更重要了,看過Spring源碼的都知道一個重要的方法叫refresh,沒錯就是在這個接口中定義的。最重要的是擴展了配置上下文的功能,和控制上下文生命周期的能力等等。

 

三、IoC容器的具體實現類 DefaultListableBeanFactory(重點)

  首先證明一點,為什麼說DefaultListableBeanFactory類是具體的實現類呢?

  隨便啟動一個SpringBoot項目找到第25行代碼(在SpringBoot的啟動流程系列博文中有介紹)

 1 public ConfigurableApplicationContext run(String... args) {
 2     //記錄程序運行時間
 3     StopWatch stopWatch = new StopWatch();
 4     stopWatch.start();
 5     // ConfigurableApplicationContext Spring 的上下文
 6     ConfigurableApplicationContext context = null;
 7     Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
 8     configureHeadlessProperty();
 9     //從META-INF/spring.factories中獲取監聽器
10     //1、獲取並啟動監聽器
11     SpringApplicationRunListeners listeners = getRunListeners(args);
12     listeners.starting();
13     try {
14         ApplicationArguments applicationArguments = new DefaultApplicationArguments(
15                 args);
16         //2、構造容器環境
17         ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
18         //處理需要忽略的Bean
19         configureIgnoreBeanInfo(environment);
20         //打印banner
21         Banner printedBanner = printBanner(environment);
22         ///3、初始化容器
23         context = createApplicationContext();
24         //實例化SpringBootExceptionReporter.class,用來支持報告關於啟動的錯誤
25         exceptionReporters = getSpringFactoriesInstances(
26                 SpringBootExceptionReporter.class,
27                 new Class[]{ConfigurableApplicationContext.class}, context);
28         //4、刷新容器前的準備階段
29         prepareContext(context, environment, listeners, applicationArguments, printedBanner);
30         //5、刷新容器
31         refreshContext(context);
32         //刷新容器后的擴展接口
33         afterRefresh(context, applicationArguments);
34         stopWatch.stop();
35         if (this.logStartupInfo) {
36             new StartupInfoLogger(this.mainApplicationClass)
37                     .logStarted(getApplicationLog(), stopWatch);
38         }
39         listeners.started(context);
40         callRunners(context, applicationArguments);
41     } catch (Throwable ex) {
42         handleRunFailure(context, ex, exceptionReporters, listeners);
43         throw new IllegalStateException(ex);
44     }
45 
46     try {
47         listeners.running(context);
48     } catch (Throwable ex) {
49         handleRunFailure(context, ex, exceptionReporters, null);
50         throw new IllegalStateException(ex);
51     }
52     return context;
53 }

  debug

  如2標註點所示IoC容器的真實面孔就是這個DefaultListableBeanFactory類了。當然他還有一個子類XmlBeanFactory,不過都已經被標註為棄用了(@Deprecated)在《Spring技術內幕》這本書裏面也是着重的講的這個類,可能當時作者是以SpringMVC項目來講解的吧。XmlBeanFactory顧名思義就是提供了對xml配置方式的支持。呃。。。又勾起了用SpringMVC的痛苦回憶。

  言歸正傳,

  如下圖,看看他的繼承關係

   章節二中提到了很多的IoC容器系列,這樣總結一下吧,俗話說一流企業做標準,二流企業做產品,章節二中的那一坨就是IoC容器的實現標準,本章節我們要總結的類DefaultListableBeanFactory就是IoC容器的具體產品。

  看見上圖中那一堆接口和類是不是有點懵,沒關係,咱們慢慢梳理一下。

 

3.1,作為IoC容器的基礎設計路線

  這條線路在上一章節中已經梳理過了。只是多出了ConfigurableListableBeanFactory接口,ConfigurableListableBeanFactory接口主要是增加指定忽略類型和接口等

 

3.2、作為IoC容器的高級設計路線

  這條設計路線乍一看還是挺複雜的,的確是這樣。

  1, BeanFactory ==> AutowireCapableBeanFactory ==> AbstractAutowireCapableBeanFactory ==> DefaultListableBeanFactory 

  在這條線路中,AutowireCapableBeanFactory接口定義了自動注入bean(autowireBean()),創建bean(createBean()),初始化bean(initializeBean())方法等。那麼真正實現這些方法的類便是AbstractAutowireCapableBeanFactory。

  AbstractAutowireCapableBeanFactory抽象類中實現了AutowireCapableBeanFactory接口定義的方法。在此基礎上通過繼承AbstractBeanFactory具備了操作Bean的能力。

  2, SingletonBeanRegistry ==> DefaultSingletonBeanRegistry ==> FactoryBeanRegistrySupport ==> AbstractBeanFactory ==> AutowireCapableBeanFactory ==> DefaultListableBeanFactory 

  這條關係鏈有點長,在這條鏈中我們要關心的是SingletonBeanRegistry接口,顧名思義,這個接口是單例Bean的註冊接口。當然也不止註冊這麼簡單。如下圖中所示,除了註冊單例之外,還定義獲取單例的方法。

  注意:為什麼只有singleton的註冊中心,而沒有prototype類型的Bean的註冊中心呢?因為單例Bean(singleton)是Spring幫我們創建的並維護的,原型Bean(prototype)是每次獲取都會創建出來一個實例。本質是不同的。

  3, AliasRegistry ==> SimpleAliasRegistry ==> DefaultSingletonBeanRegistry ==> FactoryBeanRegistrySupport ==> AbstractBeanFactory ==> AutowireCapableBeanFactory ==> DefaultListableBeanFactory 

   這條路線呢,主要是提供管理別稱的能力。因為不是重點,在此就不詳細分析了。

  4, AliasRegistry ==> BeanDefinitionRegistry ==> DefaultListableBeanFactory 

  BeanDefinitionRegistry接口要重點說一下,該接口是BeanDefinition的註冊中心。使DefaultListableBeanFactory具備操作BeanDefinition的能力。看一下它有什麼方法。

  包括了註冊,刪除,獲取BeanDefinition的方法。當然這隻是個接口,這些方法的具體實現在DefaultListableBeanFactory中。

 

3.3、DefaultListableBeanFactory幾個重要的父類和接口

3.3.1, AbstractBeanFactory 抽象類

  如上圖所示,AbstractBeanFactory中實現了BeanFactory中定義的幾個重要的方法。常用的註解 @Autowired @Resource(name = “xxx”) 大家都知道一個是按類查找,一個是按名獲取。具體實現這兩個註解的方法就是上圖中圈出來的幾個方法。幾個getBean()方法最終都進入了doGetBean()方法。doGetBean()方法是實際獲得Bean的地方,也是觸發依賴注入發生的地方。在SpringBoot啟動流程總會對這個方法進行詳細的介紹。

3.3.2, AbstractAutowireCapableBeanFactory 抽象類

  AbstractBeanFactory中實現了getBean()方法,AbstractAutowireCapableBeanFactory中實現了Bean的創建方法。

  當我們需要定義一個Bean通常會有這樣寫 @Bean(name = “test”, initMethod = “init”, destroyMethod = “destroy”) 。AbstractAutowireCapableBeanFactory中完成了一個Bean從 create(createBean()) ==> createInstance(createBeanInstance()) ==> init(invokeInitMethods()) 的所有工作。所以這個抽象類的作用不言而喻。具體的創建過程,會在SpringBoot的啟動流程中詳細介紹。

3.3.3, DefaultSingletonBeanRegistry 讓IoC容器擁有作為“容器”的能力

  其實我們經常說的Spring 容器,這個容器其實更多的是BeanFactory所代表的意義:Bean生產工廠。是滴,通常我們理解的容器應該是能裝東西的,但是spring 容器不是代表代表水桶一樣的東西,而是像富士康一樣,生產東西的地方。比如我們需要一個Bean,我們只需要告訴spring,spring就會給我們。所以到目前為止我們還沒有看到IoC作為“容器”的能力。以上言論純屬自己理解,不喜勿噴。

  DefaultSingletonBeanRegistry屬性先貼出來

 1 public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
 2     /**
 3      * Cache of singleton objects: bean name --> bean instance
 4      * 緩存 單例對象
 5      */
 6     private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);//好習慣,創建ConcurrentHashMap,指定初始化因子,縱觀Spring源碼,創建HashMap,都有初始化因子。get
 7 
 8     /**
 9      * Cache of singleton factories: bean name --> ObjectFactory
10      * 緩存 單例工廠
11      */
12     private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
13 
14     /**
15      * Cache of early singleton objects: bean name --> bean instance
16      * 緩存 提前暴露的對象
17      */
18     private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
19 
20     /**
21      * Set of registered singletons, containing the bean names in registration order
22      * 已經註冊的單例對象集合,按照註冊順序排序的,並且是不可重複的。
23      */
24     private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
25 
26     /**
27      * Names of beans that are currently in creation
28      *
29      */
30     private final Set<String> singletonsCurrentlyInCreation =
31             Collections.newSetFromMap(new ConcurrentHashMap<>(16));
32 
33     /**
34      * Names of beans currently excluded from in creation checks
35      */
36     private final Set<String> inCreationCheckExclusions =
37             Collections.newSetFromMap(new ConcurrentHashMap<>(16));
38 
39     /**
40      * List of suppressed Exceptions, available for associating related causes
41      */
42     @Nullable
43     private Set<Exception> suppressedExceptions;
44 
45     /**
46      * Flag that indicates whether we're currently within destroySingletons
47      */
48     private boolean singletonsCurrentlyInDestruction = false;
49 
50     /**
51      * Disposable bean instances: bean name --> disposable instance
52      * spring是作為一個註冊中心的樣子,在容器shutdown的時候,直接從這裏面找到需要執行destory鈎子的Bean
53      */
54     private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
55 
56     /**
57      * Map between containing bean names: bean name --> Set of bean names that the bean contains
58      * 名稱為name的bean,所持有的beans 的映射關係
59      */
60     private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
61 
62     /**
63      * Map between dependent bean names: bean name --> Set of dependent bean names
64      * 名稱為name的bean與其所依賴的bean的映射關係
65      */
66     private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
67 
68     /**
69      * Map between depending bean names: bean name --> Set of bean names for the bean's dependencies
70      */
71     private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
72 }

 

  屬性singletonObjects ,沒錯,就是這個東東,最終存儲單例(singleton)Bean的地方,在SpringBoot啟動流程中,會詳細介紹存取的過程。上面說了原型(prototype)Bean是不需要緩存的,不解釋了。到這裏我們初步看到了IoC作為“容器”該有的樣子。

  DefaultSingletonBeanRegistry上面提到的SingletonBeanRegistry接口的相關方法,並且增加了很多對單例的操作的方法。

3.3.4, DefaultListableBeanFactory (重點)

  上面我們從IoC容器的宏觀設計角度闡述了DefaultListableBeanFactory作為IoC容器的具體實現的設計思想。在這裏來分析一下這個類本身的設計。

  首先看看該類的屬性

 1 public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
 2         implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
 3     /**
 4      * Map from serialized id to factory instance
 5      * 緩存 序列化ID到 DefaultListableBeanFactory 實例的映射
 6      */
 7     private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
 8             new ConcurrentHashMap<>(8);
 9 
10     /**
11      * Optional id for this factory, for serialization purposes
12      */
13     @Nullable
14     private String serializationId;
15 
16     /**
17      * Whether to allow re-registration of a different definition with the same name
18      */
19     private boolean allowBeanDefinitionOverriding = true;
20 
21     /**
22      * Whether to allow eager class loading even for lazy-init beans
23      */
24     private boolean allowEagerClassLoading = true;
25 
26     /**
27      * Optional OrderComparator for dependency Lists and arrays
28      */
29     @Nullable
30     private Comparator<Object> dependencyComparator;
31 
32     /**
33      * Resolver to use for checking if a bean definition is an autowire candidate
34      * 被用來解決去校驗一個BeanDefinition是不是自動裝載的候選人
35      */
36     private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();
37 
38     /**
39      * Map from dependency type to corresponding autowired value
40      * 緩存 類型對應的自動裝載的Bean
41      */
42     private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);
43 
44     /**
45      * Map of bean definition objects, keyed by bean name
46      * 緩存 beanName到BeanDefinition的映射關係
47      */
48     private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
49 
50     /**
51      * Map of singleton and non-singleton bean names, keyed by dependency type
52      * 緩存 類型 和 beanName的映射關係
53      */
54     private final Map<Class<?>, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64);
55 
56     /**
57      * Map of singleton-only bean names, keyed by dependency type
58      * 緩存 類型 和 單例Bean names的映射
59      */
60     private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64);
61 
62     /**
63      * List of bean definition names, in registration order
64      * 緩存 beanDefinition name的list
65      */
66     private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
67 
68     /**
69      * List of names of manually registered singletons, in registration order
70      */
71     private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);
72 
73     /**
74      * Cached array of bean definition names in case of frozen configuration
75      */
76     @Nullable
77     private volatile String[] frozenBeanDefinitionNames;
78 
79     /**
80      * Whether bean definition metadata may be cached for all beans
81      */
82     private volatile boolean configurationFrozen = false;
83 }

  在Spring中,實際上是把DefaultListableBeanFactory作為一個默認的功能完整的IoC容器來使用。 DefaultListableBeanFactory作為一個功能完整的容器具備了除以上父類所具有功能外,還加入了對BeanDefinition的管理和維護。從上面的代碼可以看到一個重要的屬性:beanDefinitionMap。beanDefinitionMap緩存了Bean name到 BeanDefinition的映射。到這裡是不是發現了IoC容器另外一個作為“容器”的能力。在我的理解範圍內,IoC容器作為“容器”真正裝的兩個最總要的能力算是總結完了,一個是裝單例(Singleton)Bean,一個是裝BeanDefinition。

 

3.3.5, BeanDefinition 

  Spring通過定義BeanDefinition來管理基於Spring的應用中的各種對象以及他們之間的相互依賴關係。BeanDefinition抽象了我們對Bean的定義,是讓容器起作用的主要數據類型。我么都知道在計算機世界里,所有的功能都是建立在通過數據對現實進行抽象的基礎上的。IoC容器是用來管理對象依賴關係的,對IoC容器來說,BeanDefinition就是對依賴反轉模式中管理的對象依賴關係的數據抽象,也是容器實現依賴反轉功能的核心數據結構,依賴反轉功能都是圍繞對這個BeanDefinition的處理來完成的。這些BeanDefinition就像是容器里裝的水,有了這些基本數據,容器才能發揮作用。簡單一句話來說,BeanDefinition就是Bean的元數據,BeanDefinition存放了對Bean的基本描述,包括Bean擁有什麼屬性,方法,Bean的位置等等Bean的各種信息。IoC容器可以通過BeanDefinition生成Bean。

  BeanDefinition究竟長什麼樣呢?

  在同第三章debug的地方一樣,點開beanFactory,然後查看beanDefinitionMap屬性。

  OK,BeanDefinition就是長這樣了。具體怎麼通過它生成Bean,在SpringBoot啟動流程中會詳細介紹。

 

四、SpringBoot web工程中的上下文 AnnotationConfigServletWebServerApplicationContext

  在SpringBoot工程中,應用類型分為三種,如下代碼所示。

 1 public enum WebApplicationType {
 2     /**
 3      * 應用程序不是web應用,也不應該用web服務器去啟動
 4      */
 5     NONE,
 6     /**
 7      * 應用程序應作為基於servlet的web應用程序運行,並應啟動嵌入式servlet web(tomcat)服務器。
 8      */
 9     SERVLET,
10     /**
11      * 應用程序應作為 reactive web應用程序運行,並應啟動嵌入式 reactive web服務器。
12      */
13     REACTIVE
14 }

  對應三種應用類型,SpringBoot項目有三種對應的應用上下文,我們以web工程為例,即其上下文為AnnotationConfigServletWebServerApplicationContext

 1 public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot."
 2         + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
 3 public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
 4         + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
 5 public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
 6         + "annotation.AnnotationConfigApplicationContext";
 7         
 8 protected ConfigurableApplicationContext createApplicationContext() {
 9     Class<?> contextClass = this.applicationContextClass;
10     if (contextClass == null) {
11         try {
12             switch (this.webApplicationType) {
13                 case SERVLET:
14                     contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
15                     break;
16                 case REACTIVE:
17                     contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
18                     break;
19                 default:
20                     contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
21             }
22         } catch (ClassNotFoundException ex) {
23             throw new IllegalStateException(
24                     "Unable create a default ApplicationContext, "
25                             + "please specify an ApplicationContextClass",
26                     ex);
27         }
28     }
29     return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
30 }

  我們先看一下AnnotationConfigServletWebServerApplicationContext的設計。 

   在2.2中已經介紹了ApplicationContext的設計

  關於AnnotationConfigServletWebServerApplicationContext詳細的設計路線在這裏就不像DefaultListableBeanFactory容器那麼詳細的去講解了。在第二章說過,應用上下文可以理解成IoC容器的高級表現形式,拿上圖和DefaultListableBeanFactory的繼承關係圖,不難發現,應用上下文確實是在IoC容器的基礎上豐富了一些高級功能。在第二章中,我們還說過應用上下文對IoC容器是持有的關係。繼續看第二章debug的截圖,context就是AnnotationConfigServletWebServerApplicationContext的神秘面孔,他的一個屬性beanFactory就是IoC容器(DefaultListableBeanFactory)。所以他們之間是持有,和擴展的關係。

  接下來看GenericApplicationContext類

1 public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
2     private final DefaultListableBeanFactory beanFactory;
3     ...
4 }

   第一行赫然定義了beanFactory屬性,正是DefaultListableBeanFactory對象。

  關於上下文還有另外一個最重要的方法refresh,上文中說道該方法是在ConfigurableApplicationContext接口中定義的,那麼在哪實現的該方法呢?

  看AbstractApplicationContext類。

 1 @Override
 2 public void refresh() throws BeansException, IllegalStateException {
 3     synchronized (this.startupShutdownMonitor) {
 4         // Prepare this context for refreshing.
 5         //刷新上下文環境
 6         prepareRefresh();
 7 
 8         // Tell the subclass to refresh the internal bean factory.
 9         //這裡是在子類中啟動 refreshBeanFactory() 的地方
10         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
11 
12         // Prepare the bean factory for use in this context.
13         //準備bean工廠,以便在此上下文中使用
14         prepareBeanFactory(beanFactory);
15 
16         try {
17             // Allows post-processing of the bean factory in context subclasses.
18             //設置 beanFactory 的後置處理
19             postProcessBeanFactory(beanFactory);
20 
21             // Invoke factory processors registered as beans in the context.
22             //調用 BeanFactory 的后處理器,這些處理器是在Bean 定義中向容器註冊的
23             invokeBeanFactoryPostProcessors(beanFactory);
24 
25             // Register bean processors that intercept bean creation.
26             //註冊Bean的后處理器,在Bean創建過程中調用
27             registerBeanPostProcessors(beanFactory);
28 
29             // Initialize message source for this context.
30             //對上下文中的消息源進行初始化
31             initMessageSource();
32 
33             // Initialize event multicaster for this context.
34             //初始化上下文中的事件機制
35             initApplicationEventMulticaster();
36 
37             // Initialize other special beans in specific context subclasses.
38             //初始化其他特殊的Bean
39             onRefresh();
40 
41             // Check for listener beans and register them.
42             //檢查監聽Bean並且將這些監聽Bean向容器註冊
43             registerListeners();
44 
45             // Instantiate all remaining (non-lazy-init) singletons.
46             //實例化所有的(non-lazy-init)單件
47             finishBeanFactoryInitialization(beanFactory);
48 
49             // Last step: publish corresponding event.
50             //發布容器事件,結束Refresh過程
51             finishRefresh();
52         } catch (BeansException ex) {
53             if (logger.isWarnEnabled()) {
54                 logger.warn("Exception encountered during context initialization - " +
55                         "cancelling refresh attempt: " + ex);
56             }
57 
58             // Destroy already created singletons to avoid dangling resources.
59             destroyBeans();
60 
61             // Reset 'active' flag.
62             cancelRefresh(ex);
63 
64             // Propagate exception to caller.
65             throw ex;
66         } finally {
67             // Reset common introspection caches in Spring's core, since we
68             // might not ever need metadata for singleton beans anymore...
69             resetCommonCaches();
70         }
71     }
72 }

   OK,應用上下文就介紹到這裏。

 

 五、IoC容器的初始化過程

   在這裏我們先口述一下IoC容器的初始化過程吧,源碼分析,請移步SpringBoot啟動流程分析。

  簡單來說IoC容器的初始化過程是由前面介紹的refresh()方法啟動的,這個方法標志著IoC容器的正式啟動。具體來說,這個啟動包括三個過程

1 BeanDefinition的Resource定位
2 BeanDefinition的載入
3 向IoC容器註冊BeanDefinition

 

   1、第一個過程:Resource定位

  這個定位指的是BeanDefinition的資源定位,它由ResourceLoader通過統一的Resource接口完成,這個Resource對各種形式的BeanDefinition的使用都提供了統一接口。對於這些BeanDefinition的存在形式,可以是通過像SpringMVC中的xml定義的Bean,也可以是像在類路徑中的Bean定義信息,比如使用@Component等註解定義的。這個過程類似於容器尋找數據的過程,就像用水桶裝水先要把水找到一樣。

  結合SpringBoot說一下這個過程,對於SpringBoot,我們都知道他的包掃描是從主類所在的包開始掃描的,那這個定位的過程在SpringBoot中具體是這樣的,在refresh容器之前(prepareContext()方法中),會先將主類解析成BeanDefinition,然後在refresh方法中並且是掃描Bean之前,解析主類的BeanDefinition獲取basePackage的路徑。這樣就完成了定位的過程。(先不討論SpringBoot中指定掃描包路徑和自動裝配)

  2、第二個過程:BeanDefinition的載入

  這個載入過程是把用戶定義好的Bean表示成IoC容器內部的數據結構,而這個容器內部的數據結構就是BeanDefinition。

  在SpringBoot中,上面我們說到通過主類找到了basePackage,SpringBoot會將該路徑拼接成:classpath*:org/springframework/boot/demo/**/*.class這樣的形式,然後一個叫做PathMatchingResourcePatternResolver的類會將該路徑下所有的.class文件都加載進來,然後遍歷判斷是不是有@Component註解,如果有的話,就是我們要裝載的BeanDefinition。大致過程就是這樣的了。

  注意:@Configuration,@Controller,@Service等註解底層都是@Component註解,只不過包裝了一層罷了。

   3、第三個過程:註冊BeanDefinition

   這個過程通過調用上文提到的BeanDefinitionRegister接口的實現來完成。這個註冊過程把載入過程中解析得到的BeanDefinition向IoC容器進行註冊。通過上文的分析,我們可以看到,在IoC容器中將BeanDefinition注入到一個ConcurrentHashMap中,IoC容器就是通過這個HashMap來持有這些BeanDefinition數據的。比如DefaultListableBeanFactory 中的beanDefinitionMap屬性。

六、IoC容器的依賴注入

  上面對IoC容器的初始化過程進行了詳細的介紹,這個過程完成的主要的工作是在IoC容器中建立BeanDefinition數據映射。在此過程中並沒有看到IoC容器對Bean的依賴關係進行注入。依賴注入是Spring實現“控制反轉”的重要一環。Spring將依賴關係交給IoC容器來完成。

  依賴控制反轉的實現有很多種方式。在Spring中,IoC容器是實現這個模式的載體,它可以在對象生成或者初始化時直接將數據注入到對象中,也可以通過將對象注入到對象數據域中的方式來注入對方法調用的依賴。這種依賴注入是可以遞歸的,對象被逐層注入。

 

 

  原創不易,轉載請註明出處。

  如有錯誤的地方還請留言指正。

 

參考文獻:

  《Spring技術內幕–深入解析Spring框架與設計原理(第二版)》

 

【精選推薦文章】

自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"