zoukankan      html  css  js  c++  java
  • 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源码的学习笔记还在持续更新哈。

  • 相关阅读:
    LeetCode 252. Meeting Rooms
    LeetCode 161. One Edit Distance
    LeetCode 156. Binary Tree Upside Down
    LeetCode 173. Binary Search Tree Iterator
    LeetCode 285. Inorder Successor in BST
    LeetCode 305. Number of Islands II
    LeetCode 272. Closest Binary Search Tree Value II
    LeetCode 270. Closest Binary Search Tree Value
    LeetCode 329. Longest Increasing Path in a Matrix
    LintCode Subtree
  • 原文地址:https://www.cnblogs.com/mpyn/p/11781639.html
Copyright © 2011-2022 走看看