1. Spring IOC源码研究笔记(2)——ApplicationContext系列

1.1. 继承关系

非web环境下,一般来说常用的就两类ApplicationContext:

  • 配置形式为XML的:ClassPathXmlApplicationContext、FileSystemXmlApplicationContext

  • 配置形式为注解的:AnnotationConfigApplicationContext

前者的继承链为:AbstractApplicationContext -> AbstractRefreshableApplicationContext -> AbstractRefreshableConfigApplicationContext -> AbstractXmlApplicationContext。

后者的继承链为:AbstractApplicationContext -> GenericApplicationContext。

注意,AbstractRefreshableConfigApplicationContext实现了InitializingBean(回调为如果自己没有启动,那么就refresh)、BeanNameAware(表明如果它自己作为bean的时候,将会知道自己的bean name)。

GenericApplicationContext还实现了BeanDefinitionRegistry接口:委托给自己持有的DefaultListableBeanFactory实现。

1.2. ApplicationContext

接口,继承了:ListableBeanFactory、HierachicalBeanFactory、ResourcePatternResovler、MessageSource、ApplicationEventPublisher、EnvironemntCapable。

  • 返回:ID、name、display name、启动时间、父app ctx、自己内部的autowire capable bean factory

1.3. ConfigurableApplicationContext

继承自ApplicationContext、LifeCycle、Closable。

  • 添加了一些配置的功能:

    • 设置id

    • 设置parent app ctx

    • 设置、获取environment

    • 设置class loader

    • 添加protocol resolver(来自DefaultResourceLoader)

    • 添加bean factory post processor

    • 添加application listener

  • 然后是涉及生命周期的一些方法:

    • refresh

    • close

    • 添加shutdown hook

    • 判断是否active

1.4. AbstractApplicationContext

AbstractApplicationContext实现了ConfigurableApplicationContext接口。

AbstractApplicaitonContext继承于DefaultResourceLoader从而实现了ApplicationContext要求的ResourceLoader的方法。

AbstractApplicationContext通过实例字段持有一个ResourcePatternResolver实例(构造函数中使用自身作为resource loader来new一个PathMatchingResourcePatternResolver),从而通过委托的方式实现了ApplicationContext要求的ResourcePatternResolver的方法。

AbstractApplicationContext通过实例字段持有一个MessageSource实例(initMessageSource时初始化),从而通过委托的方式实现了ApplicationContext要求的MessageSource中的方法。

AbstractApplicationContext的子类通过持有DefaultListableBeanFactory(AbstractRefreshableApplicationContext、GenericApplicationContext),从而实现了ApplicationContext、ConfigurableAppliationContext暴露BeanFactory的功能。

AbstractApplicationContext通过实例字段持有LifeCycleProcessor从而通过委托的方式实现了AplicationContext要求的Lifecycle中的方法。

AbstractApplicationContext通过实例字段持有ApplicationEventMulticaster从而实现了ApplicationContext要求的ApplicationEventMulticaster中的方法。

由上可见,对于ApplicationContext、ConfigurableApplicationContext中定义的方法其实就refresh值得看,其他的方法要不就是简单的set方法,要么就是其他的接口而来,被委托给其他类或者继承自其他类,从而实现。

1.4.1. refresh方法

refresh方法是ApplicationContext最重要的方法了,它定义了


1.4.1.1. prepareRefresh

一个ConfigurableApplicationContext可能被refresh多次:

做了三件事:

  • 设置状态字段:active(true)、closed(false)、startUpDate(currentTimeMillis)

  • 属性相关:initPropertySource(在Web环境下的ApplicationContext中重写)、验证标记为required的属性是否都可以解析。

  • 消息发布相关:把applicationListeners设置为第一次refresh之前的样子。创建一个空的“早期事件容器”(registerListener步骤之前发布的事件会暂存在这个容器中,在registerListener的时候,取出所有ApplicationListener添加到ApplicationEventMulticaster中,并发布这些暂存的事件,并将该容器置null。)

1.4.1.2. obtainFreshableBeanFactory

refreshBeanFactory是个抽象方法,在子类中实现。

  • 对于GenericApplicationContext来说,它的构造函数中就将DefaultListableBeanFactory给new出来了,并持有它,refreshBeanFactory只是给它设置id、以及设置标记位。

  • 对于AbstractRefreshableApplicationContext来说:它销毁旧bean factory,然后创建一个新的bean factory,设置id后,就从xml文件中加载bean definition。

1.4.1.3. prepareBeanFactory

对beanfactory进行了一些配置:

  • 配件设置:

    • 设置beanfactory的bean classloader(application自己的,ResourceLoader中的方法——如果显式set的话就用set的,没有的话使用当前线程的context classloader,还没有的话就用ClassUtils的classloader,再没有的话就用system classloader)

    • bean expression resovler(StandardBeanExpressionResolver)

    • property editor registrar(ResourceEditorRegistrar)。

  • 依赖设置:

    • 忽略掉bean一些依赖:EnvironmentAware、ResourceLoaderAware、ApplicationEventPublisherAware、ApplicationContextAware、MessageSourceAware、EmbeddedValueResovlerAware(就算bean依赖于这些类型的bean,也不为它注入这些依赖)。

    • 添加一些现成就能用的依赖:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicaitonContext(如果bean依赖这些类型的bean,那么直接将本AbstractApplicationContext将作为候选的注入值),当有bean依赖这种类型的bean时,直接返回本application context

  • 添加bean post processor:ApplicationContextAwareProcessor、ApplicationListenerDetector。

  • 如果存在loadTimeWeaver这个名称的bean(跟aop相关,类加载时织入),那么给beanfactory设置tempclass loader(bean factory的bean classloader创建一个ContextTypeMatchClassLoader),同时添加一个LoadTimeWeaverAwareProcessor(bpp,他给LoaderTimeWeaver类型的bean设置LoadTimeWeaver,这个LoadTimeWeaver来自bean factory)。

  • 将environment中的一些数据封装为单例bean实例注册到beanfactory中。

1.4.1.4. postProcessBeanFactory

在AbstractApplicationContext中为空方法,在一些web环境下的子类中被重写。

1.4.1.5. invokeBeanFactoryPostProcessors

如果bean factory没有tempClassLoader但注册了LoadTimeWeaver bean,那么就添加一个LoadTimeWeaverAwareProcessor这个bpp,然后设置一个ContextTypeMatchClassLoader作为tmpClassLoader。

接下来由于逻辑太长比较复杂,专门抽到PostProcessorRegistrationDelegate类中:

传入的beanFactoryPostProcessor是此前其他组件直接调用AppCtx的addBeanFactoryPostProcessor添加的bean factory postprocessor(在SpringBoot的情况下有:CachingMetadataReaderFactoryPostProcessor、ConfigurationWarningsPostProcessor、PropertySourceOrderingPostProcessor)。

另外一部分bean factory post processor是作为bean definition注册在bean factory中。(AnnotationConfigApplicationContext在构造时会构造AnnotatedBeanDefinitionReader,进而会往添加一些用于注解处理的bfpp。)

  • 如果bean factory实现了BeanDefinitionRegistry接口(一般来说肯定走这个逻辑,因为只有一个DefaultListableBeanFactory):

    1. 遍历传入的bean factory post processor,将其中的bean definition registry post processor和一般的bean factory post processor分开,并用其中的bean definition registry post processor的postProcessBeanDefinitionRegistry方法处理app ctx的bean factory

    2. 然后再从app ctx的bean factory中取出所有PriorityOrdered类型的bean definition registry post processor类型的bean,排序后,用其postProcessBeanDefinitionRegistry方法依次处理app ctx的bean factory,并将其保存下来以供去重。

    3. 然后再从app ctx的bean factory中取出所有只是Ordered类型的没用过的bean definition registry post processor类型的bean,排序后,用其postProcessBeanDefinitionRegistry方法依次处理app ctx的bean factory,并将其保存下来以供去重。

    4. 然后再取出剩下的普通优先级没用过的bean definition registry post processor,用其postProcessBeanDefinitionRegistry方法r处理app ctx的bean factory。注意由于postProcessBeanDefinitionRegistry时可能会向bean factory中注册新的bean definition registry post processor类型的bean,因此用广度优先的方式进行不断的迭代,直到没有为止。

    5. 将上面的这些bean definition registry post processor按照同样的顺序,使用其postProcessBeanFactory方法来处理bean factory

    6. 将入参中的bean factory post processor的postProcessBeanFactory方法处理bean factory。

  • 如果bean factory没有实现BeanDefinitionRegistry接口(基本不可能走这里):

    • 遍历入参中的bean factory post processor来postProcessBeanFactory。

最后取出BeanFactory中的BeanFactoryPostProcessor类型并且没用过的Bean,按照PriorityOrdered -> Ordered -> Regular优先级的顺序对BeanFactory进行后处理。

注意:

  1. 对于PriorityOrdered、Ordered优先级的后处理器,它们在处理前都需要进行排序,而regular优先级的不用排序。

  2. 对BDRPP和BFPP的处理不相同,BDRPP在后处理时可能引入新的BDRPP,因此采用广度优先搜索的方式,需要多次遍历Bean Factory中的BDRPP。而BFPP,直接遍历一次Bean Factory中的BFPP,不进行广度优先搜索。

  3. 上面的排序,一般使用AnnotationAwareOrderComparator来排序:实现了PriorityOrdered -> 实现了Ordered -> Spring的@Order -> javax的@Priority

整体的顺序为:

  1. 非bean方式的BDRPP先postProcessBeanDefinitionRegistry

  2. bean方式的并且是PriorityOrdered的BDRPP,排序后,postProcessBeanDefinitionRegistry

  3. bean方式的并且是Ordered的BDRPP,排序后,postProcessBeanDefinitionRegistry

  4. bean方式的非Ordered的BDRPP,postProcessBeanDefinitionRegistry

  5. 上面的BDRPP,相同顺序来postProcessBeanFactory

  6. 非bean方式的BDRPP,postProcessBeanFactory

  7. bean方式的并且是PriorityOrdered的BFPP,排序后,postProcessBeanFactory

  8. bean方式的并且是Ordered的BFPP,排序后,postProcessBeanFactory

  9. bean方式的非Ordered的BFPP,postProcessBeanFactory




1.4.1.6. registerBeanPostProcessors

由于方法太长,委托给PostProcessorRegistrationDelegate这个静态工具类处理。

添加一个BeanPostProcessorChecker。

再将隐式的以bean形式存在于容器中的BeanPostProcessor取出来:

  • 先取出PriorityOrdered类型的,排序后,显示地添加到bean factory中(addBeanPostProcessor)

  • 再取出Ordered类型的,排序后,显示地添加到bean factory中

  • 再取出普通优先级的,显示地添加到bean factory中

  • 如果上面的BeanPostProcessor还是MergedBeanDefinitionPostProcessor(被称为internal bean post processor),排序后,再进行一次addBeanPostProcessor(注意这个操作会首先remove然后再add,相当于是说,如果再后面又add了一个之前add过的,就只是把这个往后挪而已。)

  • 最后添加一个ApplicationListenerDetector(将其放在最后)。


1.4.1.7. initMessageSource

如果本地beanfactory中没有messageSource这个bean,那么new一个DelegatingMessageSource设置好它的parent后(DelegatingMessageSource是HierachicalMessageSource,这个parent要么是当前app ctx的父app ctx内部的MessageSource,要么就是父app ctx),交给application context,同时将其作为单例注册到bean factory中。

1.4.1.8. initApplicationEventMulticaster

ApplicationContext实现了ApplicationEventPublisher接口,这个功能的实现是借助ApplicationEventMulticaster的(作为字段)。

这个字段的实例化在initApplicationEventMulticaster方法中。

如果本地beanFactory中有ApplicaitonEventMulticaster类型的bean,那么就采用这个bean。(注意一定是本地beanFactory,因为父级beanFactory在发布事件的时候子beanFactory添加的ApplicationListener不会接收到,而子beanFactory发布的事件,父beanFactory的application listener会接收到,见publishEvent方法)。

如果本地beanFactory中没有注册这个bean的话,原地实例化一个SimpleApplicationEventMulticaster作为字段,并将其作为singleton注册到beanFactory中。

1.4.1.9. onRefresh

在AbstractApplicationContext中为空方法,在一些web环境下的子类中被重写。

1.4.1.10. registerListeners

appctx的ApplicationListener来源有两个:

  • addApplicationListener时添加的ApplicationListener

  • 自己管理的ApplicationListener类型的bean。

将appctx的application listener添加到appctx的ApplicationEventMulticaster中,使用它发布earlyApplicationEvent(appctx还没有refresh的时候它的publishEvent方法就被调用)。

1.4.1.11. finishBeanFactoryInitialization

结束对bean factory的初始化:

  1. 如果bean factory中包含conversion service类型的bean,并且bean name为“conversionService,那么就给bean factory设置conversion service

  2. 如果bean factory没有设置embedded value resolver(StringValueResolver),那么就用一个lambda表达式当它的StringValueResovler,它的功能就是说,根据appctx的environment的resolvePlaceholders方法来解析字符串

  3. 提前实例化所有LoadTimeWeaverAware类型的bean

  4. 设置bean factory的tmpClassLoader为null

  5. 然后冻结bean factory的configuration(DefaultListableBeanFactory)。

  6. 提前实例化bean factory中所有singleton object

1.4.1.12. finishRefresh

  • clearResourceCaches:DefaultResourceLoader中的方法。

  • initLifecycleProcessor:如果bean factory中有LifecycleProcessor类型的bean并且bean name为“lifecycleProcessor“,就将其取出作为当前app ctx的lifecycleProcessor,否则就使用app ctx内部的bean factory来new一个DefaultLifecycleProcessor,将其作为bean注册到bean factory中,并设置它为appct的lifecycleProcessor。

  • 调用lifecycleProcessor的onRefresh方法。

  • 发布ContextRefreshedEvent

1.4.1.13. resetCommonCaches

1.4.2. registerShutdownHook方法

这个shutdown hook的作用就是在应用退出的时候,调用App Ctx的close方法。

1.4.3. close方法

只有当前还是active时,才会close:

  1. CAS设置closed状态位,发布ContextClosedEvent

  2. 调用lifecycle processor的onClose方法

  3. 销毁bean factory中的所有单例bean

  4. 关闭BeanFactory——在子类中重写:

    • 对于GenericApplicationContext来说,只是将持有的BeanFactory的setSerializationId设置为null,依旧持有这个BeanFactory

    • 对于AbstractRefreshableApplicationContext来说,它不仅将BeanFactory的setSerializationId设置为null,而且不再持有改beanfactory

  5. 将applicationListeners恢复到第一次refresh之前的状态

  6. 设置active标记位

  7. 移除shutdown hook


1.4.4. publishEvent方法

如果发布的event不是ApplicationEvent,那么包装成PlayloadApplicationEvent。

如果此时ApplicationContext还没有registerListener,那么将event暂存,否则就将Event通过自己持有的AppliationEventMulticaster发布出去,告知每个ApplicationListner。

最后将event发布到父级Application Context中(子容器发布的事件被父容器感知,父容器发布的事件子容器不感知)。



1.5. GenericApplicationContext

GenericApplicationContext实现了BeanDefinitionRegistry这个接口,这个接口的功能委托给自己持有的DefaultListableBeanFactory(在构造函数中new这个bean factory)。

GenericApplicationContext还第一次提供了一系列registerBean这个方法,这些方法根据传入的bean类型来构造出ClassDerivedBeanDefinition,然后registerBeanDefinition(BeanDefinitionRegistry中的方法,交给bean factory来实现。)

GenericApplicationContext继承AbstractApplicationContext,但是它可以设置ResourceLoader。

如果它设置了ResourceLoader的话,那么它对于ResourceLoader接口中的方法实现交给自己持有的resourceLoader,没有的话才由父类实现。

对于ResourcePatternResolver中的方法,当它持有的resourceLoader是ResourcePatternResolver时,才委托,否则也是调用父类的同名方法。

注意:GenericApplicationContext的构造函数中不会调用refresh方法。

1.6. AnnotationConfigApplicationContext

AnnotationConfigApplicationContext实现了AnnotationConfigRegistry接口,它持有AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner(在构造函数中根据this创建。),这两个配件是用来从Java Class中读取注解配置来生成bean definition。

AnnotationConfigRegistry接口定义了两个功能:

  • 扫描给定的package下的Component Class(scan方法)

  • 注册给定的Component Class。(register方法)

scan方法委托给自己持有ClassPathBeanDefinitionScanner,register方法委托给AnnotatedBeanDefinitionReader。

重写了AbstractApplicationContext的setEnvironment方法:还将给定的environment设置到AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner中去。

还又添加了两个配件的设置方法:

  • ScopeMetadataResolver

  • BeanNameGenerator

这两个配件都是设置到AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner中了。

重写了GenericApplicationContext引入的registerBean方法,将其最终交给AnnotatedBeanDefinitionReader实现而不是让bean factory来实现。

注意:构造AnnotationConfigApplicationContext的时候,只有提供class类型或者package名,才会调用register或者scan方法来加载bean definition并调用refresh方法,如果这两者都不提供,那么是不会refresh的,这时需要我们在手动的调用scan或register方法,再手动的refresh

1.7. AbstractRefreshableApplicationContext

定义了allowBeanDefinitionOverridingallowCircularReferences这两个参数字段,这两个字段是用来设置自己持有的bean factory的。

AbstractRefreshableApplicationContext主要是重写了AbstractApplicationContext中的refreshBeanFactory这个方法。

这个方法在obtainFreshableBeanFactory中的被调用。

refreshBeanFactory的逻辑是:

  • 销毁旧的bean factory(将其中的单例bean销毁,然后不再持有旧bean factory)

  • 创建带层级的bean factory(DefaultListableBeanFactory,parent bf 从 parent app ctx中来)。

  • 给bean factory设置序列化id为app ctx的id。

  • allowBeanDefinitionOverriding、allowBeanDefinitionOverriding这两个字段如果设置了,设置bean factory

  • 将bean definition加载到bean factory中(在子类中重写)。



1.8. AbstractRefreshableConfigApplicationContext

AbstractRefreshableConfigApplicationContext添加了:添加配置文件所在路径的功能,传入的配置文件路径,经过environment的的通配符解析替换之后,保存在一个String数组中。

然后它同时实现了InitializingBean、BeanNameAware接口。

  • afterPropertiesSet方法中:如果没有active,那么就refresh

  • setBeanName方法中:如果没有设置id,那么就设置id为这个bean name,并且设置display name

1.9. AbstractXmlApplicationContext

AbstractXmlApplicationContext主要重写了AbstractRefreshableApplicationContext的loadBeanDefinitions方法。

使用XmlBeanDefinitionReader来生成bean definition然后注册到bean factory中。

这些配置来来源有两个:

  • 一是AbstractRefreshableConfigApplicationContext中的配置文件路径数组

  • 二是子类重写的getConfigResources返回的Resource类型的数组。



1.10. ClassPathXmlApplicationContext

ClassPathXmlApplicationContext内部又持有了ClassPathResource数组类型作为配置文件的来源。

注意,在构造ClassPathXmlApplicationContext时,提供了配置路径信息后,才会自动refresh。

Spring IOC源码研究笔记(2)——ApplicationContext系列的更多相关文章

  1. spring IOC源码分析(ApplicationContext)

    在上一篇文章中,我们以BeanFactory这条主线进行IOC的源码解析的,这里,将以ApplicationContext这条线进行分析.先看使用方法: @Test public void testA ...

  2. Spring系列(三):Spring IoC源码解析

    一.Spring容器类继承图 二.容器前期准备 IoC源码解析入口: /** * @desc: ioc原理解析 启动 * @author: toby * @date: 2019/7/22 22:20 ...

  3. Spring Ioc源码分析系列--Ioc的基础知识准备

    Spring Ioc源码分析系列--Ioc的基础知识准备 本系列文章代码基于Spring Framework 5.2.x Ioc的概念 在Spring里,Ioc的定义为The IoC Containe ...

  4. Spring Ioc源码分析系列--前言

    Spring Ioc源码分析系列--前言 为什么要写这个系列文章 首先这是我个人很久之前的一个计划,拖了很久没有实施,现在算是填坑了.其次,作为一个Java开发者,Spring是绕不开的课题.在Spr ...

  5. Spring Ioc源码分析系列--Ioc源码入口分析

    Spring Ioc源码分析系列--Ioc源码入口分析 本系列文章代码基于Spring Framework 5.2.x 前言 上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了I ...

  6. Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析

    Spring Ioc源码分析系列--Ioc容器BeanFactoryPostProcessor后置处理器分析 前言 上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析已经介绍到Ioc容器 ...

  7. Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理

    Spring Ioc源码分析系列--Ioc容器注册BeanPostProcessor后置处理器以及事件消息处理 前言 上一篇分析了BeanFactoryPostProcessor的作用,那么这一篇继续 ...

  8. Spring Ioc源码分析系列--@Autowired注解的实现原理

    Spring Ioc源码分析系列--@Autowired注解的实现原理 前言 前面系列文章分析了一把Spring Ioc的源码,是不是云里雾里,感觉并没有跟实际开发搭上半毛钱关系?看了一遍下来,对我的 ...

  9. Spring Ioc源码分析系列--自动注入循环依赖的处理

    Spring Ioc源码分析系列--自动注入循环依赖的处理 前言 前面的文章Spring Ioc源码分析系列--Bean实例化过程(二)在讲解到Spring创建bean出现循环依赖的时候并没有深入去分 ...

随机推荐

  1. PAT B1061判断题

    题目描述: 判断题的评判很简单,本题就要求你写个简单的程序帮助老师判题并统计学生们判断题的得分. 输入格式: 输入在第一行给出两个不超过 100 的正整数 N 和 M,分别是学生人数和判断题数量.第二 ...

  2. python---单链表的常用操作

    class Node(object): """结点""" def __init__(self, data): self.data = dat ...

  3. spring-bean依赖注入-02(通过p命名空间注入)

    上一篇博客讲述了为什么使用spring依赖注入,怎么注入,详见 spring-bean依赖注入-01(等你来点击) 废话不多说,开始使用p命名空间进行set注入 使用另外一种注入方式是这样的(具体实现 ...

  4. 十、包机制与JavaDoc

    一.包机制 为了更好的组织类,Java提供了包机制,用于区别类名的命名空间. 包语句的语句格式为: package pkg1[. pkg2[. pkg3...]]; 一般使用公司域名倒置作为包名:例如 ...

  5. 『忘了再学』Shell基础 — 9、Bash中的特殊符号(一)

    目录 1.双单引号 2.双引号 3.$符号 4.反引号 5.$()符号 6.#符号 7.\符号 1.双单引号 '':单引号.在单引号中所有的特殊符号,如$和"`"(反引号)都没有特 ...

  6. 基础设施即代码(IAC),Zalando Postgres Operator UI 入门

    Postgres Operator UI 提供了一个图形界面,方便用户体验数据库即服务.一旦 database 和/或 Kubernetes (K8s) 管理员设置了 operator,其他团队就很容 ...

  7. 在 WinForms 项目中使用全局快捷键

    借助于全局快捷键,用户可以在任何地方操控程序,触发对应的功能.但 WinForms 框架并没有提供全局快捷键的功能.想要实现全局快捷键需要跟 Windows API 打交道.本文就交你如何使用 Win ...

  8. 基于pgrouting的路径规划处理

    对于GIS业务来说,路径规划是非常基础的一个业务,一般公司如果处理,都会直接选择调用已经成熟的第三方的接口,比如高德.百度等.当然其实路径规划的算法非常多,像比较著名的Dijkstra.A*算法等.当 ...

  9. 【FAQ】HMS Core广告服务:如何获取正式广告位ID以及流量变现的受限情况

    HMS Core广告服务开发指南中提到"xxxx为测试专用的广告位ID,App正式发布时需要改为正式的广告位ID",那么今天咱们就来说说,怎么获取正式的广告位ID. 测试广告位ID ...

  10. SpringBoot扩展点EnvironmentPostProcessor

    一.背景 之前项目中用到了Apollo配置中心,对接Apollo配置中心后,配置中心的属性就可以在程序中使用了,那么这个是怎么实现的呢?配置中心的属性又是何时加载到程序中的呢?那么我们如果找到了这个是 ...