码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 【Spring源码】自动注入·类型:autowireByType()详解


    【Spring源码系列- IOC】

    1

    【Spring源码】0.安装Gradle环境

    2

    【Spring源码】1.下载与编译_pom relocation to an other version number is not f

    3

    【Spring源码】2.试个水先~Debug找到传说中的三级缓存(图解向,堆图预警)

    4

    【Spring源码】3. xml文件如何转换成BeanDefinition(主要涉及prepareRefresh()+ obtainFreshBeanFactory()两个函数,图解向,堆图预警)_spring xml转bean

    5

    【Spring源码】4. 自己搞个标签?~自定义标签保姆级全过程(图解向,堆图预警)

    6

    【Spring源码】5.spring的bean工厂准备工作(prepareBeanFactory(beanFactory)

    7

    【Spring源码】6. Spring扩展自定义属性编辑器保姆级教程

    8

    【Spring源码】7. 如何添加自定义的BeanFactoryPostProcessor

    9

    【Spring源码】8. 捋下invokeBeanFactoryPostProcessors()主要处理流程

    10

    【Spring源码】9. 超级重要的ConfigurationClassPostProcessor

    11

    【Spring源码】10. 递归调用的processConfigurationClass()方法

    12

    【Spring源码】11. 我是注解类不?checkConfigurationClassCandidate()注解类判断方法详解

    13

    【Spring源码】12. 注册bean处理器registerBeanPostProcessors()

    14

    【Spring源码】13. 国际化处理initMessageSource()源码解析

    【补充内容】【保姆级】SpringBoot项目中的i18n国际化

    15

    【Spring源码】14. 消息多播器(观察者模式)

    【补充内容】【保姆级示例向】观察者模式

    16

    【Spring源码】15. Bean的创建过程(1.概述篇)

    17

    【Spring源码】16. Bean的创建过程(2)

    18

    【Spring源码】17.创建Bean这篇认真的@(・●・)@

    【补充内容】

    【保姆级·创建对象】如何通过Supplier创建对象

    【保姆级·创建对象】如何通过factory-method创建对象

    【保姆级·创建对象】如何利用resolveBeforeInstantiation()在预处理阶段返回一个Bean的实例对象

    19

    【Spring源码】18. factory-method创建对象关键函数详解:instantiateUsingFactoryMethod()

    20

    【Spring源码】19. 没合适的构造器?找determineCandidateConstructors()!

    21

    【Spring源码】20. MergedBeanDefinitionPostProcessor修改/合并bean定义

    【补充内容】

    【保姆级】@PostConstruct & @PreDestroy使用示例

    【Spring源码】AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition()详解

    【Spring源码】CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition()详解

    22

    【Spring源码】21. 初探循环依赖

    【补充内容】

    【保姆级】手把手Debug循环依赖的整体流程

    【实践向】当移除了三级缓存……

    【分析向】没有三级缓存会导致什么?

    【Spring源码】插播一个创建代理对象的wrapIfNecessary()方法

    23

    【Spring源码】22. 属性填充populateBean()详解

    【补充内容】

    【Spring源码】自动注入·名称:autowireByName()详解

    【Spring源码】自动注入·类型:autowireByType()详解

    【Spring源码】属性值的解析与赋值:populateBean().applyPropertyValues()

    【保姆级】超超超简单的自定义注解实现@Autowired同款功能

    24

    【Spring源码】23. 执行初始化逻辑:initializeBean()

    本文目录

    resolveDependency()

    doResolveDependency()

    resolveMultipleBeans()


    进入方法autowireByType()

    ​首先获取工厂的自定义类型转换器,如果获取到没有配置自定义类型转换器就使用bw作为类型转换器

    插播个问题:为什么类型为BeanWrapper的bw可以赋值给类型为TypeConverter的converter?

    因为BeanWrapper继承了ConfigurablePropertyAccessor,而ConfigurablePropertyAccessor继承了TypeConverter

    定义一个集合用于存放所有候选Bean的Name,接下来的unsatisfiedNonSimpleProperties()方法和autowiredByName()中的一样用于筛选属性(即同时满足有写入属性方法 && 未被排除在依赖检查项之外 && pvs中没有该pd的属性名 && 该pd的属性类型不是“简单类型”) 接下来同样的,遍历属性名数组,先从bw中获取propertyName对应的PropertyDescriptor 判断bean对象是否是PriorityOrder实例,并根据结果给eager赋值,eager为true时会导致初始化Lazy-init单例和由FactoryBeans(或带有"factory-bean"引用的工厂方法)创建的对象以进行类型检查

    随后将methodParam封装成AutowireByTypeDependencyDescriptor并赋值给desc,通过调用resolveDependency()方法,根据desc的依赖类型解析出与descriptor所包装的对象匹配的候选Bean对象,紧接着判断获取到的对象autowiredArgument是否为空,不为空则将当前遍历到的propertyName和刚获取到的autowiredArgument对象分别作为键、值添加进pvs 最后像上面autowireByName()方法一样,遍历所有候选BeanNames,依次注册propertyName、beanName的依赖关系 我们在上一篇文章分析过autowireByName()方法(【自动注入·名称】autowireByName()详解_AQin1012的博客-CSDN博客)

    将autowireByType()和autowireByName()这两个方法做下对比,可以发现整体流程大致相同,都是一个for♻️循环遍历经过unsatisfiedNonSimpleProperties()方法筛选过的propertyNames,而且都有将符合要求的propertyName和根据propertyName获取到对象作为键值对放入pvs,并调用方法registerDependentBean()进行autowiredBeanName、beanName依赖关系的注册,但是主要有以下几点区别:

    • 根据propertyName获取到对象不同
      • autowireByName()是通过getBean(propertyName)获取到对象
      • autowireByType()是通过resolveDependency(desc, beanName, autowiredBeanNames, converter)获取到对象
    • 调用方法registerDependentBean()进行注册遍历的对象不同
      • autowireByName()注册的是propertyName、beanName的依赖关系
      • autowireByType()注册的是autowiredBeanName、beanName的依赖关系

    而autowiredBeanName是从集合autowiredBeanNames中依次获取的,而autowiredBeanNames是方法autowireByType()在一开始定义了一个空的集合(下图1处)

    那么问题来了:autowiredBeanNames是在哪里被赋值的呢?

    ​从上图我们可以看到,autowiredBeanNames在序号1处被定义成了一个空集合,在序号2处作为参数传入方法resolveDependency()中,序号3处被遍历,序号4处清空,所以autowiredBeanNames中的值只能是在方法resolveDependency()中被添加的 resolveDependency() 进入方

    resolveDependency()

    ​选择一个他的实现类,我这里选的是DefaultListableBeanFactory(emmm...好像也没啥别的可选)

    ​可以看到autowiredBeanNames传入了方法doResolveDependency()中

    doResolveDependency()

    进入方法doResolveDependency()

    此方法是用于解descriptor所包装的对象匹配的候选bean对象,有几段逻辑值得分析分析

    ​获取descriptor的依赖类型,并尝试使用descriptor的默认值作为最近候选的Bean对象

    随后使用此BeanFactory的自动装配候选解析器获取descriptor的默认值,如果该值不为空则继续判断是否为String类型,是的话则进行解析(如果是表达式则会进一步进行解析),在获取beanName的合并后的RootBeanDefinition,随后评估该bd中包含的value,如果刚刚解析出来的strVal是一个表达式,则会再对其进行解析

    接着判断传入的参数typeConverter是否为空,如果为空就调用getTypeConverter()方法获取工厂的类型转换器,最后返回将value转换为type的实例对象 如果上面的过程未return,则会尝试针对desciptor所包装的对象类型是(stream、数组、Collection类型且对象类型是接口、Map)的情况进行解析与依赖类型匹配的候选Bean对象,并将其封装成相应的依赖类型对象,通过resolveMultipleBean()方法获取multipleBeans,如果获取到的multipleBeans不为空则将其返回

    ​可以看到autowiredBeanNames传入了方法resolveMultipleBeans()中

    resolveMultipleBeans()

    进入resolveMultipleBeans()方法

    ​看(*≧ω≦)~autowiredBeanNames就是在方法resolveMultipleBean()中被赋值的

    我们再回到再回到doResolveDependency()方法中

    ​接下来的逻辑是尝试获取与type匹配的唯一候选bean对象,查找与type匹配的候选bean对象,构建成Map,如果构建的Map对象为空,则继续判断descriptor是否需要注入,如果需要则抛出异常,不需要则返回空表示未找到候选的bean对象

    在确定Map不为空后,分别定义了用于存放候选的beanName和候选bean对象的两个变量autowiredBeanName和instanceCandidate,接下来是通过一个if判断分支为这两个变量赋值:

    • 如果候选bean对象的Map不止一个,那就需要从中确定一个可以自动注入的最佳的beanName赋值给autowiredBeanName,如果没获取到这样的beanName,并且descriptor是需要注入/type不是数组或者集合类型,则让descriptor尝试选择一个实例(默认返回NoUniqueBeanDefinitionException异常)最后根据获取的autowiredBeanName从matchingBeans中获取bean对象,赋值给instanceCandidate
    • 如果候选bean对象的Map只有一个的话,尝试获取该唯一值(map为空的时候已经在前面抛出异常或者返回了),随后分别将键、值赋给autowiredBeanName和instanceCandidate

    最后返回获取到的bean对象!

    结尾习惯性撒个花(。・ω・。)ノ🎉

  • 相关阅读:
    【语音识别】声学建模中基于树的状态绑定
    Windows下Pytorch环境配置(Anaconda、CUDA、CUDNN、Pytorch)
    年薪6万美元/应届医学博士赴加州大学圣迭戈分校博士后就职
    精品基于Java的社区团购系统SSM
    矩阵论—线性子空间、生成子空间、核空间、零度、子空间的交与和、直和
    Netty实战(五)
    leetcode最大间距(桶排序+Python)
    【数模系列】01_聚类分析
    Hbase之SnapShot快照操作
    如何学习渗透测试?怎样才能入行?
  • 原文地址:https://blog.csdn.net/aqin1012/article/details/127846109
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号