Spring配置类后置处理器ConfigurationClassPostProcessor解析配置类的过程

本文将继续跟着前文中的配置类判断进行,方法入口位于 org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>) ,在这里,已注册bean定义中的配置类bean定义已经被筛选到候选对象集合中,逐个进行遍历解析。

  1. 如果bean实现自 AnnotatedBeanDefinition 接口,则进行注解bean定义的解析流程,详见下面章节中的各步骤说明。
  2. 如果bean实现自 AbstractBeanDefinition 接口,且指定了beanClass 属性,则进行抽象bean定义的解析;
  3. 否则作为常规配置类进行解析
  4. 逐个遍历解析完毕配置类之后,判断是否需要延迟导入,如果存在需要延迟导入的处理器,直接进行处理。

1. 注解bean定义 AnnotatedBeanDefinition 的解析流程

实际调用到 ConfigurationClassParser#processConfigurationClass 方法进行配置类解析,这里的参数是将元数据和bean名称进行封装成为一个整体

  1. 首先判断是否指定了 @Conditional 注解及子注解,需要在解析环境进行跳过
  2. 判断给定的bean类是否在给定的类集合中
    1. 如果已经存在,则判断bean类是否是通过 @Import 导入的,且已存在的bean类也是通过 @Import 导入的,则直接将当前判断的bean类合并到已经存在的bean类中,直接返回。
    2. 如果已经存在,且当前判断的bean类不是通过 @Import 导入的,则从给定的类集合中删除。
  3. 根据bean定义中的类,解析得到源类 SourceClass
  4. 调用方法 ConfigurationClassParser#doProcessConfigurationClass 执行处理配置类的动作,并最终返回父类,递归进行处理,直到最顶级的父类
    1. 判断配置类是否添加了 @Component 注解,如果添加了,则还需要解析成员类,也就是嵌套类,这里我们先不讨论
    2. 如果配置类添加了 @PropertySources 注解,并且配置类解析器的环境变量实现了 ConfigurableEnvironment 接口,则调用 ConfigurationClassParser#processPropertySource 方法处理 @PropertySources 注解,详细解析过程见下面章节

1.4.2 ConfigurationClassParser#processPropertySource 方法处理 @PropertySources 注解的过程

  1. 获取 @PropertySources 注解中的 name 属性值,如果未指定,则参数值为 null
  2. 获取 @PropertySources 注解中的 encoding 属性值,如果未指定,则参数值为 null
  3. 获取 @PropertySources 注解中的 value 属性值,解析为位置数组,如果未指定,则直接判定为失败,必须要指定 value 属性
  4. 获取 @PropertySources 注解中的 ignoreResourceNotFound 属性值,默认为false,判断是否忽略找不到资源的错误。
  5. 获取 @PropertySources 注解中的 factory 属性值,作为属性源工厂 PropertySourceFactory 的类名,默认为 PropertySourceFactory.class。根据属性值创建属性源工厂实例对象,值为 PropertySourceFactory.class 时,工厂为 DefaultPropertySourceFactory 实例。
  6. 遍历上面第3步得到的位置数组,将位置解析成为绝对地址,并加载可用资源,进行解码后,使用工厂实例创建属性源对象,添加到属性源中
  7. 获取 @ComponentScan 注解的属性值,如果指定了该注解,且在注册bean REGISTER_BEAN 时不需要跳过,则遍历属性值
    1. 使用组件扫描解析器扫描给定的包名路径,如何完成扫描请查看下面的章节
    2. 遍历扫描出来的bean定义集合,如果bean定义存在源定义则使用源定义进行进一步判断,判断bean定义是否是配置类,如果是配置类,则继续调用解析配置类方法进行递归解析
  8. 调用方法 ConfigurationClassParser#processImports 处理 @Import 注解(详细的处理过程,将另开文章进行说明
  9. 获取 @ImportResource 注解,读取 location ,替换 location 中的参数值,读取 reader 属性值,解析为bean定义读取器类,保存对应的映射关系到配置类的导入资源集合中
  10. 解析配置类,得到配置类中使用 @Bean 注解的方法集合,将获取到的方法元数据创建为 BeanMethod ,保存到配置类的 BeanMethod 属性中(具体的注解方法扫描过程,我们另开文章说明
  11. 递归遍历处理接口上的默认 @Bean 方法
  12. 递归处理超类,如果超类不为空,且不是 java 开头,则添加到已知超类集合中,返回超类,继续递归遍历处理
  13. 返回null

1.4.2.7.1 ComponentScanAnnotationParser#parse 解析配置对象,并扫描指定包下bean对象的流程

  1. 创建扫描器 ClassPathBeanDefinitionScanner,这也可以证明在声明上下文时声明的扫描器是给程序员用的,Spring自己使用的时候会重新创建一个新的扫描器,这个扫描器在创建的时候会判断是否使用默认的过滤器。
  2. 获取指定的Bean名称生成器,如果给定的生成器类型为 BeanNameGenerator ,认为使用的是内部默认的名称生成器,否则使用给定的名称生成器类,创建对应生成器实例,并绑定到扫描器上。
  3. 判断指定的范围代理选项 scopedProxy ,指定是使用JDK代理还是CGLIB代理,或是不使用代理,并保存使用代理的选项。如果不使用范围代理,需要获取范围解析选项 scopeResolver ,并实例化解析器对象绑定到扫描器上。
  4. 将设置的资源模式 resourcePattern 绑定到扫描器上
  5. 将设置的包含类型 includeFilters 和排除类型 excludeFilters 绑定到扫描器上
  6. 将设置的延迟加载选项 lazyInit 设置在扫描器中的bean定义的默认值上
  7. 获取设置的扫描包名属性 basePackages ,替换其中的环境变量值,并将值转换为数组,多个包名之间可以使用 ,; \t\n 进行分隔,将获得的包名集合保存到包地址集合中
  8. 获取指定的包类名属性值 basePackageClasses ,获取其包名保存在包地址集合中
  9. 如果最终包地址集合仍然没有值,则默认添加当前配置类所在的包名
  10. 添加排除过滤器,用于排除扫描结果中类名跟配置类类名一直的结果,避免配置类重复加载
  11. 调用 ClassPathBeanDefinitionScanner#doScan 执行实际包扫描的动作。(实际扫描包的流程,见下文说明
  12. 返回扫描出来的bean定义集合。

1.4.2.7.1.11 ClassPathBeanDefinitionScanner#doScan 执行实际包扫描的流程

  1. 声明bean定义集合
  2. 遍历包名数组,扫描包下符合条件的候选Bean定义集合,遍历候选Bean定义集合(具体的扫描过程我们另开文章再来说明
    1. 使用范围源数据解析器判断是否添加了 @Scope 注解,如果设置了该注解,以该bean定义中设置的范围和代理类型为准,否则使用默认的单例范围。并将解析出来的范围信息保存在bean定义中
    2. 使用bean名称生成器生成Bean名称,常规做法是直接根据类名,将首字母小写后作为bean名称,内部类名称中会产生 .
    3. 如果bean定义实现了 AbstractBeanDefinition 接口,需要进一步处理bean定义:
      • 设置bean定义默认值,是否延迟加载、自动装配模式、依赖检查、初始化方法名称、销毁方法名称为默认值,且说明指定的初始化方法和销毁方法均不是默认的
      • 如果指定了自动装配候选规则,则使用规则同名称进行匹配,如果匹配成功,则认为该bean作为自动装配候选,否则不可以作为自动装配候选。
    4. 如果bean定义实现了 AnnotatedBeanDefinition 接口,则处理bean定义中的基本注解,设置到bean定义中。具体有:
      • @Lazy 注解,指定是否需要延迟初始化
      • @Primary 注解,指定当多个bean满足自动装配的条件时,当前bean是否作为优先选择对象
      • @DependsOn 注解,指定当前bean的依赖对象,需要在依赖bean初始化之后初始化,销毁之后销毁
      • @Role 注解,指定角色类型
      • @Description 注解,指定说明内容
    5. 检查扫描出来的bean定义同现有的已经扫描出来的bean定义是否冲突,如果返回false,则直接跳过该扫描出来的bean,否则创建一个新的bean定义并添加到bean定义集合中。(添加方法为 BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry) ,具体的执行逻辑将另开文章说明
      • 如果bean定义集合中不存在给定名称的bean定义,则直接认为不冲突
      • 得到已经存在的bean定义,判断是否存在源bean定义,如果存在源,则使用源来进行比较
      • 比较已经存在bean定义跟新扫描出来的bean定义是否兼容,比较的内容有:原有bean定义没有实现 ScannedGenericBeanDefinition 接口,也就说原有bean不是扫描得到的;扫描文件中的文件源不为空,且文件源同原有文件的文件源一致,也就是说同一个源文件被扫描出来两次;原有bean定义和扫描出来的bean定义对象一致,也就是等价类被扫描两次。满足上面条件的认为是兼容的,直接返回false,不继续处理扫描出来的这个对象
      • 否则抛出 ConflictingBeanDefinitionException 异常,存在冲突的bean定义。
  3. 返回处理完毕的bean定义集合
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335