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源码的学习笔记还在持续更新哈。

  • 相关阅读:
    梦断代码阅读笔记之一
    市场调研
    站立会议第九天
    站立会议第八天
    站立会议第七天
    站立会议第六天
    站立会议第五天
    团队项目第一阶段冲刺站立会议6(4月23日)
    团队项目第一阶段冲刺站立会议5(4月22日)
    团队项目第一阶段冲刺站立会议4(4月21日)
  • 原文地址:https://www.cnblogs.com/mpyn/p/11781639.html
Copyright © 2011-2022 走看看