天天看点

spring-ioc源码

spring-ioc源码

需要解决的问题
  1. beanfactory和factorybean的区别
  2. beanfactorypostprocessor在spring中的作用
  3. springioc的加载过程
  4. bean的生命周期
  5. spring中有哪些扩展接口及调用时机

大纲

1. 主要流程-springioc的加载过程
  1. 实例化容器AnnotationConfigApplicationContext
  2. 实例化工厂DefaultListAbleBeanFactory
  3. 实例化建立beanDefination读取器 annotatedBeandefinationReader
  4. 创建beandefination扫描器:classPathBeanDefiantionScanner
  5. 注册配置类为beanDefination: register方法,annotatedClasses
  6. refresh
  7. invokeBeanfacorypostProcessors(beanfactory)
  8. finishBeanfactoryinitialization(beanfactory)
2. 主要流程-spring bean的生命周期

springioc的加载过程

  1. 首先准备个例子如下(插入图片)
  2. 2.springioc的容器加载流程
  3. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);      

    annotationConfigApplicationContext的关系图如下

    创建annotationConfigApplicationContext对象

    首先看这个构造方法

  4. ​ 3.4 如果没有配置类会字节返回

    ​ 3.5 处理排序

    ​ 3.6 解析配置类,可能是full类,也可能是lite类

    ​ 3.7 在第六步的时候只是注册了部分Bean,但是如@Import@Bean,是没有被注册的。

    因为可以有多个配置类,所以需要循环处理。我们的配置类的BeanDefinition是AnnotatedBeanDefinition的实例,所以会进入第一个

    if

    重点在于doProcessConfigurationClass方法,需要特别注意,最后一段代码,会把configClass放于一个map,

  1. 这是一个有参构造方法,可以接受多个配置类,不过一般情况只传入一个配置类。
  2. 这个配置类有俩种情况,一种是传统意义上的带@Configutarion注解的配置类,还有一种是带@Component,@import,@importResource等注解的配置类,在spring中前者被称为full配置类,后者叫lite配置类,也叫普通bean

    查看这个this()方法都干了什么,如图

    spring-ioc源码
    spring-ioc源码
    首先无参构造方法中就是对读取器read和扫描器进行了实例化,reader的类型是annotatedbeandefinitionReader,可以看出他是一个 bean定义读取器,scanner的类型是classpathbeandefinitionscanner,它仅仅是外面手动调用.scan方法,
  3. 实例化工厂:defaultListableBeanFactory
    spring-ioc源码
    DefaultListableBeanFactory的关系图
    spring-ioc源码
    defaultlistablebeanfactory是bean的工厂,是用来生产和获得bean。
  4. 实例化建beanDefinition读取器:annotatedbeandefinitionReader

    主要负责俩件事

    1. 注册内置beanPostprocrssor
      1. 注册相关的beandefinition
    spring-ioc源码

    ​ 回到annotationconfigapplicationcontext的无参构造方法,看annotatedbeandefinitionreader

    spring-ioc源码
    这里beandefinitionregistry是annotationconfigapplicationcontext的实例,这里调用其他类构造方法
    spring-ioc源码
    spring-ioc源码
    spring-ioc源码
    这个方法就是注册spring的内置的多个bean
    1. 判断容器内是否存在configurationclasspostprocessor bean
      1. 如果不存在,就通过rootBeanDefinition的构造方法获得configurationclasspostprocessor的beandefinition,rootbeandefinition是beandefinition的子类
      2. 执行registerpostprocessor方法,注册bean
    5 beandefinition是什么?
    spring-ioc源码
    1. beanmetadataelement接口,beandefinition元数据,返回该bean的来源
    2. attributeaccessor接口,提供对beandefinition属性操作能力

      spring-ioc源码
    3. 他是用来描述bean,里面存放着关于bean的一些列信息,比如bean的作用域,bean所对应的class,是否懒加载,是否primary等等。
    4. registerpostprocessor
      spring-ioc源码
      这方法为beandefinition设置了role,ROLE_INFRASTRUCTURE代表这是spring内部的,并非用户定义的。然后又调用了registerBeandefinition方法,他的实现类是defaultlistablebeanfactory:
      spring-ioc源码
      spring-ioc源码
      从这里可以看出defaultlistablebeanfactory就是我们所说的容器,里面放着beandefinitionmap,beandifinitionnames,前者呢beanname做key,beandefinition做value,后者是个集合,里面放beanname。
      spring-ioc源码
      ConfigurationClassPostProcessor实现了beandefinitionregistrypostprocessor接口,beandefinitionregistryPostProcessor又扩展了beanfactorypostprocessor接口,beanfactorypostprocessor是spring的扩展点之一,configurationclasspostprocessor是spring的重要类
      spring-ioc源码
      除了注册configurationclass后置处理器还注册了其他bean,比如bean后置处理器,也是spring的扩展点之一。至此,实例化annotatedbeandefinitionreader reader完毕。
  5. 创建beandefinition扫描器,classpathbeandefinitionscanner
    1. 由于常规方法不会用到annotationconfigapplicationcontext里的scanner。这里的canner仅仅是为了程序员手动调用里面的scan方法
  6. 注册配置类为beandefinition: register(annotatedclasses)
    spring-ioc源码
    1.  传入的参数是数组      
    spring-ioc源码
    spring-ioc源码
    spring-ioc源码
    这里需要注意的是以常规的方式去注册配置类,此方法除了第一个参数,其他参数都是默认值。
    1. 通过annotatedGenericBeanDefinition的构造方法,获取配置的bean定义,在注册configutationClass后置处理器也是通过构造方法获取bean定义,只是通过rootbeandefinition,现在是annotatedGenericBeanDefinition中获得
    2. 判断需不需要跳过注册,spring中有一个@Condition,如果不满足条件就会跳过这个类注册
    3. 解析作用域,如果没设置就默认为单例
    4. 获取beanname
    5. 解析通用注解,填充到annotatedGenericBeanDefinition,解析的注解为lazy,primary,dependsOn,role,Description
    6. 限定符处理,除了@Qualifier,还有primary,lazy
    7. 把annotatedGenericBeanDefinition数据结构和beanname封装到一个对象中
    8. 注册,最终会调用defaultlistableBeanFactory中的registerBeanDefinition方法注册
    spring-ioc源码
    spring-ioc源码
    spring-ioc源码
  7. refresh()方法
    spring-ioc源码
    spring-ioc源码
    spring-ioc源码
    1. prepareRefresh

      主要是做一些刷新前的准备工作,和主流程关系不大,主要是保存容器的启动时间,启动标志

    2. configurablelistablebeanfactory beanfactory = obtainFreshBeanFactory()

      和主流程关系不大,主要是把beanfactory取出来,xml模式下到这里会读取beanDefinition

    3. prepareBeanFactory

      添加了俩个后置处理器 applicationContextAwareProcessor,applicationListenerDetector,还设置了忽略自动装配 和允许自动装配的接口,如果不存在某个bean的时候,spring就自动注册singleton Bean.以及bean表达式解析器

      spring-ioc源码
      spring-ioc源码
      spring-ioc源码
      主要的操作如下
      1. 设置了类加载器
      2. 设置了bean表达式解析器
      3. 添加了属性编辑器的支持
      4. 添加了一个后置处理器:applicationContextAwareProcessor,此后置处理器实现了beanpostprocessor接口
      5. 设置了一些忽略自动装配的接口
      6. 设置了一些允许自动装配的接口,并且进行了复制操作
      7. 在容器中还没有xxbean的时候,帮我们注册beanName为xx的单例bean
    4. postProcessBeanFactory(beanFactory)

      空方法,以后可能会被扩展

    5. invokeBeanFactoryPostProcessors(BeanFactory)
      spring-ioc源码
      看这个小方法
      spring-ioc源码
      这里获得的是beanFactorypostprocessor,可以手动添加一个后置处理器,而不是交给spring去扫描
      spring-ioc源码
      只有这样,这个集合才不会为空。
      spring-ioc源码
      spring-ioc源码
      spring-ioc源码
      spring-ioc源码
      spring-ioc源码
      spring-ioc源码
      spring-ioc源码
      spring-ioc源码
      spring-ioc源码
      spring-ioc源码
      首先判断beanFactory是不是beandefinitionRegistry的实例,然后执行一下操作
      1. 定义一个set,装beanName,然后根据这个set,来判断后置处理器是否被执行过
      2. 定义俩个list,一个是regular后置处理器,用来装beanFactory后置处理器,一个是registryProcessors用来装载beanDefinitionRegistry后置处理器,其中bean定义注册后置处理器扩展了beanFactory后置处理器,bean定义注册后置处理器有俩个方法,一个是独有的后置处理器bean定义注册方法,一个是父类的后置处理器beanFactory方法
      3. 循环传入的beanfactory后置处理器,一般情况下都是空的,唯有手动加入beanFactory后置处理器。才有数据。因为bean定义注册后置处理器扩展了beanFactory后置处理器,所以这里要判断是不是bean定义注册后置处理器,是的话就执行后置处理器bean定义注册方法,然后把对象封装到registryprocessory中,不是的话就封装到regular后置处理器中
      4. 定义一个临时变量currentRegistryprocessors,用来装bean定义注册后置处理器
      5. getBeanNamesforType,通过类型找到beannames,从beanDefinitionNames去找,举个例子,如果这里传入了beanDefinitionregistryPostProcessor.class,就会找到类型为该类型的后置处理器。并且赋值给postProcessoryNames.一般情况下只会找到一个,就是internalconfigurationAnnotationProcessor,也就是confurationAnnotationProcess.如果你实现了bean定义后置处理器接口,也打上了@Component注解,但是在这里没有获取到,因为spring扫描实在configurationClass后置处理器类中完成的,也就是下面的invokeBeanDefinitionRegistry后置处理器方法。
      6. 循环postProcessorNames,是internalConfigurationAnnotationProcessor,判断此后置处理器是否实现了priorityordered接口。如果实现了,就把它添加到currentregistryProcessors中。再放入processedBeans,代表这个后置处理器已经被处理过了。
        spring-ioc源码
      7. 进行排序,priorityOrdered是一个排序接口,如果实现了它说明后置处理器是有顺序的,要排序,目前只有configurationClass后置处理器
      8. 把currentRegistyprocessors合并到registryprocessors,因为一开始spirng只会执行beanDefinitionRegistry后置处理器的独有方法,而不执行父类方法,所以需要把这些后置处理器放入到一个集合中,后续统一执行beanFactoryProcessor接口中的方法。
      9. 可以理解为执行currentRegistryprocessors中的中的ConfigurationClass后置处理器中的postProcessBeanDefinitionRegistry方法,这里体现了spring的设计思想,热插拔。spring很多东西交给插件去做,如果不想用,就不添加
        spring-ioc源码
      10. 清空currentRegistryProcessors,因为currentRegistryProcessors是一个临时变量,完成了目前的任务,所以清空,后面还会用到
      11. 再次根据bean定义注册后置处理器获得beanname,然后进行循环,看这个后置处理器是否被执行过了,如果没有执行,也实现了order接口,就把此后置处理器推送到currentRegistryprocessors和processedBean中。这里就可以获得我们定义的并且搭上了@Component注解的后置处理器。因为spring完成了扫描,但是需要注意由于ConfigurationClassPostProcessors上面已经被执行了,虽然可以通过getBeanNameForType获得,但是并不会加入到currentRegistryProcessors和processedBeanas.
      12. 处理排序
      13. 合并processors,合并的理由和上面一样
      14. 执行我们自定义的beandefinitionregistry后置处理器
      15. 临时清空变量
      16. 在上面的方法中,仅仅是执行了实现orderer接口的bean定义注册后置处理器,这里是执行没有实现ordered接口的bean定义注册后置处理器。
      17. 上面的代码时执行子类独有的方法,这里需要把父类的方法也执行一次
      18. 执行regular后置处理器中的后置处理器方法,需要注意的时,在一帮情况下,regular后置处理器,只有在外面手动添加beanFactory后置处理器才有数据。
      19. 查找实现了beanFactory后置处理器,并且执行了后置处理器中的方法。如下图
        spring-ioc源码
        spring-ioc源码
        spring-ioc源码
        spring-ioc源码
        spring-ioc源码
        spring-ioc源码
        1. 获取所有的beanName,放入candidateNames数组      
        1. 循环candidateNames数组,根据beanName获取beanDefinition,判断是否被处理过了。
        2. 判断是否是配置类,如果是,加入到configCandidates数组,在判断的时候还会标记配置类属于full还是lite配置类。

        3.1 当我们注册配置类的时候,可以加@Configuration,也可以加@Component,@ComponentScan,@Import,@ImportResource等注解,spring称这种配置为lite配置类,如果加了@Configuration就称之为full配置类。

        3.2 如果我们注册了lite配置类,getBean时会发现就是原本的配置类,如果我们注册full配置类,getbean时是一个cglib代理类。

        spring-ioc源码
        spring-ioc源码
        3.3 写一个A类,其中一个构造方法是打印”你好“ ,在一个配置类,有俩个被@Bean注解的方法,其中一个方法return new A(),命名为getA(),另一个方法调用getA(),如果配置类是lite配置类,会发现打印俩次你好,也就是说A类被new了俩次,如果配置类是Full类,会发现只打印一次”你好“,也就是说A类只被new了一次,因为这个类被cglib代理了,方法已经被改写。
spring-ioc源码
spring-ioc源码
spring-ioc源码
spring-ioc源码
spring-ioc源码
  1. 递归处理内部类,一般不会使用内部类。
  2. 处理@PropertySource注解,@ProertySource注解用来加载properties文件
  3. 获得componentscan注解具体的内容,componentscan注解出了最常用的basePackage之外,还有includeFilters,excludeFilters等
  4. 判断有没有被@ComponentScans标记,或者被@Condition条件带过,如果满足条件的话,进入if,进行如下操作。
  1. 执行扫描操作,吧扫描出来的放入set.
  2. 循环set,判断是否是配置类,是的话递归调用parse方法,因为被扫描出来的类,还是一个配置类,有@ComponentScan注解,或者其中有被@Bean标记的方法,等等,所以需要被再次解析。
  1. 处理@Import注解,他有三种情况,一种是import普通类,一种是importSelector,还有一种是 importBeanDefinitionRegistrar,getImports(sourceClass)是获得import的内容,返回的是一个set
  2. 处理@ImportResource注解
  3. 处理@Bean的方法,可以看到获得了带有@Bean的方法后,不是马上转化称beanDefinition,而是先用一个set接受
spring-ioc源码
spring-ioc源码
spring-ioc源码
spring-ioc源码
  1. 定义了一个扫描器scanner,常规方法中,实际上执行扫描只会是这里的scanner对象
  2. 处理includeFilters,就是把规则添加到scanner
  3. 处理excludeFilters,就是把规则添加到scanner.
  4. 解析basepackages,获得需要扫描哪些包。
  5. 添加一个默认的排除规则,拍出自己
  6. 执行扫描

这里有一个补充说明,添加规则的时候,只是把具体的规则放入规则类的集合中去,规则类是一个函数式接口,之定义了一个虚方法的接口被称为函数式接口,这里只是把规则放进去,并没有真正执行这些规则。

spring-ioc源码
spring-ioc源码

因为basePackages可能有多个,所以需要循环处理,最终会进行bean的注册,看一下findCandidateComponents

spring-ioc源码

spring支持component索引技术,需要引入一个组件,大部分项目没引进,索引会进入scanCandidateComponents方法

spring-ioc源码
spring-ioc源码
spring-ioc源码
spring-ioc源码
spring-ioc源码
spring-ioc源码
spring-ioc源码
spring-ioc源码

直到这里,才把configurationClassPostProcessor中的processConfigBeanDefinitions方法简单过一下,这里只会解析@Import的bean,不会注册。

processConfigBeanDefinitions是BeanDefinitionRegistryPostProcessor接口中的方法,beanDefinitionRegistry后置处理器扩展了beanfactoryPostProcessor.postProcessBeanFactory是判断配置类是lite还是full,如果是full就会被cglib代理,目的是保证bean的作用域

spring-ioc源码

总结:configurationClassPostProcessor中的processConfigBeanDefinitions主要是完成扫描,最终注册我们定义的bean.

6.6 registerBeanPostProcessors(beanFactory)

实例化和注册beanfactory中扩展了beanPostProcessor的bean.

例如autowriedAnnotationBeanPostProcessor(处理被@Autowired修饰的bean并注入)

requiredAnnotataionBeanPostProcessor(处理被@Requied注解修饰的方法)

commonAnnotationBeanPostProcessor(处理@PreDestory,@Resource等多个注解的作用)等

spring-ioc源码

6.7 initMessageSource()

初始国际化资源处理器

6.8 initapplicationEventMulticaster()

//创建事件多播器

spring-ioc源码

6.9 onRefresh()

模板方法,在容器刷新的时候可以自定义逻辑,不同的spring容器做不同的事情

6.10 registerListeners()

注册监听器,广播early application events

6.11 finishBeanfactoryInitialization(Beanfactory)

实例化所有剩余的单例,懒加载除外

比如invokeBeanfactorypostprocessorts方法中根据注解解析出来的的类,在这个时候都会被初始化。

实例化的过程各种beanpostProcessor开始起作用。

spring-ioc源码

上面这个方法是实例化懒加载单例bean的,也就是我们创建的bean

spring-ioc源码

再看finishBeanFactoryInitialization这个方法,里面有一个beanFactory.preInstantiateSingletons()方法,顶进去之后一个接口,它的实现类是defaultListableBeanFactory,找到里面的getBean方法,这个方法 有一个分支,如果bean是fanctoryBean,对应处理。。。。如果bean不是factoryBean,对应处理。。。。不过不管是不是fanctoryBean最终都会调用getBean方法,直接调用doGetBean.

spring-ioc源码
spring-ioc源码

这里面有一个createBean方法,有一个实现类为abstractAutowireCapableBeanFactory

spring-ioc源码

创建实例

spring-ioc源码

填充属性

spring-ioc源码
spring-ioc源码

aware系列接口回调

spring-ioc源码
spring-ioc源码
spring-ioc源码
spring-ioc源码

springBean的生命周期

网上流传的一段

  1. 实例化bean对象,没有属性,
  2. 填充属性
  3. 如果bean实现了beanNameaware接口,调用setBeanName方法
  4. 如果bean实现了beanClassLoaderAware接口,则调用setBeanClassLoader方法
  5. 如果bean实现了beanFactoryAware接口,调用setBeanFactory方法
  6. 调用beanpostProccessor的postProcessBeforeInitialization方法。
  7. 如果bean实现了initializingBean接口,调用afterPropertiesSet方法
  8. 如果bean定义了Initializating接口,调用bean的init-method
  9. 调用beanpostProcessor的postProcessAfterInitialization方法。进行到这里,bean已经准备就绪,停留在应用的上下文中,知道被销毁
  10. 如果上下文被销毁了,如果bean实现了disposableBean接口,则调用destory方法,如果bean定义了destory-method声明了销毁方法也会被调用
为了验证上面的逻辑,可以做个试验: 

首先定义了一个Bean,里面有各种回调和钩子,其中需要注意下,我在SpringBean的构造方法中打印了 

studentService,看SpringBean被new的出来的时候,studentService是否被注入了;又在 

setBeanName中打印了studentService,看此时studentService是否被注入了,以此来验证,Bean是何时 

完成的自动注入的(这个StudentServiceImpl 类的代码就不贴出来了,无非就是一个最普通的Bean)      
spring-ioc源码
spring-ioc源码
spring-ioc源码
spring-ioc源码
spring-ioc源码

6-12 finishRefresh()

refresh做完之后需要做的事情

清楚上下文资源缓存(如扫描中的asm元数据)

初始化上下文的生命周期处理器,并刷新。

发布contextRefreshedEvent事件并告知对应的applicationListener进行相应的操作

spring-ioc源码
spring-ioc源码
spring-ioc源码
spring-ioc源码

2.使用时间广播器广播事件到相应的监听器multicastEvent

spring-ioc源码

3.2调用监听器invokeListener

spring-ioc源码
spring-ioc源码

这样,当spring执行到finishrefresh方法时,就会将contextrefreshedEvent事件推送到myrefreshedListener中。跟contextRefreshedEvent相似的还有::ContextStartedEvent、ContextClosedEvent、

ContextStoppedEvent,有兴趣的可以自己看看这几个事件的使用场景。当然也可以自定义监听事件,只需要继承applicationContextEvent抽象类即可

不会,我可以学;落后,我可以追赶;跌倒,我可以站起来!