spring源碼學習(一)


 最近在學習spring源碼,把自己的學習筆記記錄一下,分享出來,如果有理解錯的,也希望各位能提出來,大家一起學習

 首先spring源碼的入口方法:

 1 public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
 2     //在this()中調用父類的方法  創建了 DefaultListableBeanFactory(這就是平常說的springbean工廠)
 3     this();
 4     //把annotatedClasses(AppConfig.java)放到spring容器中,最底層調用的是DefautListableBeanFactory.registerBeanDefinition()方法,將配置類put到beanDefinitionMap中
 5     register(annotatedClasses);
 6     refresh();
 7 }
 8 
 9 //這個方法是this()調用的
10 public AnnotationConfigApplicationContext() {
11     /**
12      * 創建一個讀取注解的bean定義讀取器
13      * bean定義其實就是beanDefinition
14      * 在這個方法里面 聲明了六個比較重要的bean,並將這個幾個bean存到了beanDefinitionMap里面
15      * CommonAnnotationBeanPostProcessor
16      * RequiredAnnotationBeanPostProcessor
17      * AutowiredAnnotationBeanPostProcessor
18      * ConfigurationClassPostProcessor
19      */
20     this.reader = new AnnotatedBeanDefinitionReader(this);
21     /**
22      * 實際上完成包掃描並不是這里的scanner完成的。而是spring在掃描包的時候,又重新new了一個ClassPathBeanDefinitionScanner完成的
23      * 這里的scanner是為了程序員能夠在外部調用annotationConfigApplicationbContext對象
24      */
25     this.scanner = new ClassPathBeanDefinitionScanner(this);
26 }

 

在spring初始化過程中,最重要的方法就是refresh()方法,在refresh中完成了bean的掃描、初始化、以及AOP動態代理對象的生成等等,我們來一點一點分析

 1 public void refresh() throws BeansException, IllegalStateException {
 2     synchronized (this.startupShutdownMonitor) {
 3         // Prepare this context for refreshing.
 4         //准備工作包括設置啟動時間、是否激活標志位、初始化屬性源配置
 5         prepareRefresh();
 6 
 7         // Tell the subclass to refresh the internal bean factory.
 8         //返回一個factory
 9         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
10 
11         // Prepare the bean factory for use in this context.
12         //准備工廠
13         prepareBeanFactory(beanFactory);
14 
15         try {
16             // Allows post-processing of the bean factory in context subclasses.
17             postProcessBeanFactory(beanFactory);
18 
19             // Invoke factory processors registered as beans in the context.
20             /**
21              * TODO
22              * 完成對bean的掃描,將beanDefinition存到map中
23              *
24 26              *
27              * 在這個方法中,注入bean,分為了三種
28              * 一、普通bean:@Component注解的bean30              *
31              * 1.獲取到所有的beanFactoryPostProcessor
32              * 2.執行 bean后置處理器的postProcessBeanFactory(configurationClassPostProcessor),該方法會把beanFactory作為入參傳到方法里面
33              * 3.從beanFactory中獲取到所有的beanName   打斷點看一下 org.springframework.context.annotation .ConfigurationClassPostProcessor#processConfigBeanDefinitions
34              *
35              * 4.然后將所有的bean包裝成beanDefinitionHolder,在后面又根據beanName和bean的metadata包裝成了ConfigurationClass
36              * 5.把所有包含@ComponentScan的類取出來,遍歷每一個componentScan,調用 ClassPathBeanDefinitionScanner.doScan(basePackages)方法
37              * 6.在doScan方法中,會遍歷basePackages,因為一個ComponentScan中可以配置多個要掃描的包
38              * 7.獲取每個包下面的 *.class文件,registerBeanDefinition(definitionHolder, this.registry); 這個方法底層就是調用org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition方法  把當前bean put到beanDefinitionMap中
39              *
40              * 二、是通過ImportSelector注解注入的bean
41              *
42              * 三、ImportBeanDefinitionRegistrar注入的bean
43              */
44             invokeBeanFactoryPostProcessors(beanFactory);
45 
46             // Register bean processors that intercept bean creation.
47             //注冊beanPostProcessor;方法里面調用的是 beanFactory.addBeanPostProcessor(postProcessor);
48             registerBeanPostProcessors(beanFactory);
49 
50             // Initialize message source for this context.
51             initMessageSource();
52 
53             // Initialize event multicaster for this context.
54             /**
55              * 注冊一個多事件派發器
56              * 先從beanFactory獲取,如果沒有,就創建一個,並將創建的派發器放到beanFactory中
57              */
58             initApplicationEventMulticaster();
59 
60             // Initialize other special beans in specific context subclasses.
61             onRefresh();
62 
63             // Check for listener beans and register them.
64             /**
65              * 注冊所有的事件監聽器
66              * 將容器中的時間監聽器添加到 applicationEventMulticaster 中
67              */
68 
69             registerListeners();
70 
71             // Instantiate all remaining (non-lazy-init) singletons.
72             /**
73              * TODO
74              * 完成對bean的實例化
75              */
76             finishBeanFactoryInitialization(beanFactory);
77 
78             // Last step: publish corresponding event.
79             /**
80              * 當容器刷新完成之后,發送容器刷新完成事件
81              * publishEvent(new ContextRefreshedEvent(this));
82              */
83             finishRefresh();
84         }
85 
86         catch (BeansException ex) {
87             
88         }
89 
90         finally {
91             // Reset common introspection caches in Spring's core, since we
92             // might not ever need metadata for singleton beans anymore...
93             resetCommonCaches();
94         }
95     }
96 }

在refresh方法中,個人覺得比較重要的是

invokeBeanFactoryPostProcessors(beanFactory);
finishBeanFactoryInitialization(beanFactory);

其他幾個方法,還沒來得及進行深入的研究,本次主要來分析這兩個方法
首先說,一個bean要在spring中完成初始化,簡單來說,分為兩步(當前,這是個人目前的理解)
1.首先要把bean掃描到容器中,然后再將bean實例化;invokeBeanFactoryPostProcessors這個方法個人理解:主要是完成了bean的掃描,將要注入的bean掃描到beanDefinitionMap中;這個map是用來存放
spring中要實例化的bean,key值是beanName,value值是一個beanDefinition對象,beanDefinition存放的是對bean的一些描述信息;
2.當bean被掃描到beanDefinitionMap中之后,就需要調用bean的構造方法等來實例化,然后在調用bean的初始化方法來進行初始化;以及bean的屬性注入等;這個工作是在finishBeanFactoryInitialization方法中完成;

一、
我們首先來說在invokeBeanFactoryPostProcessors方法中是如何完成bean的掃描的;
先說結論吧:spring在將bean注入到容器中,有幾種方式:
1.@Component注解+@ComponeneScan注解
2.@Import注解注入,import注入的話分兩種,一種是注入ImportSelector,一種是注入ImportBeanDefinitionRegistrar
3.@Bean 在配置類中,直接通過該注解將bean注入到容器中

1.那我們首先來說@Component注解這種注入方式的源碼:
由於這里的調用鏈比較長,所以直接截圖放出來,不一個一個手打了;

 

 

 

在前面,spring將配置類注入到了beanDefinitionMap中,在這個方法里面,會獲取到配置類;

 然后獲取到配置類的ComponentScan注解的值,由於ComponentScan可以指定多個包,所以會循環每個包,在doScan方法中獲取到當前包下所有的*.class文件;將掃描的文件,put到beanDefinitionMap中

這是通過掃描注入bean的方式原理

 

2.那通過ImportSelector和importBeanDefinitionRegistrar方式注入的bean,是這樣注入的

  org.springframework.context.annotation.ConfigurationClassParser#processImports

  在這個方法中完成了對這兩種方式注入的bean的掃描;方法中,會區分是ImportSelector.class還是ImportBeanDefinitionRegistrar.class;

  稍微說一下這兩個接口的區別:

   ①.ImportSelector接口中selectImports方法返回的是一個String[] 數組對象,返回數組中,包含的是要注入的bean的全類名

   ②.ImportBeanDefinitionRegistrar接口中的方法可以直接注冊bean,因為接口中的方法可以得到BeanDefinitionRegisty對象

 我們接着說注入:對於ImportSelector注入的bean會存入到configurationClasses中;對於ibdr(importbeanDefinitionRegistrar)注入的bean會存到importBeanDefinitionRegistrars中

在處理完這些之后,在 org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions方法中,對import注入的bean和@Bean注入的bean進行注入

 

 1 private void loadBeanDefinitionsForConfigurationClass(
 2         ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
 3 
 4     if (trackedConditionEvaluator.shouldSkip(configClass)) {
 5         String beanName = configClass.getBeanName();
 6         if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
 7             this.registry.removeBeanDefinition(beanName);
 8         }
 9         this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
10         return;
11     }
12 
13     //這里是對importSelector注入的bean進行初始化
14     if (configClass.isImported()) {
15         registerBeanDefinitionForImportedConfigurationClass(configClass);
16     }
17 
18     //@Bean 注解需要注入的bean對象
19     for (BeanMethod beanMethod : configClass.getBeanMethods()) {
20         loadBeanDefinitionsForBeanMethod(beanMethod);
21     }
22 
23     loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
24     //這里是對ImportBeanDefinitionRegistrar注入的bean進行初始化
25     loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
26 }

  這個方法執行完之后,所有的bean就被注入到了beanDefinitionMap中

  在invokebeanFactoryPostProcessors方法中,還有很多細節,比如對配置類上加@Configuration注解與不加@Configuration注解的處理,對importSelector注入bean的遞歸調用等等。,如果展開了,不知道該怎么寫,細節點太多,太碎,所以本次只把主干流程簡單說了一下,對於源碼解讀的注釋,可以參考本人在GitHub上傳的注釋來看。https://github.com/mapy95/spring-sourceCode

這上面是spring的源碼學習的一些注釋,對於spring源碼的學習筆記還在持續更新哈。

 


免責聲明!

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



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