【Spring源码分析】推断构造方法

推断构造方法源码解析

  • 一、确认候选构造——AutowireAnnotationBeanPostProcessor#determineCandidateConstructors
  • 二、autowireConstructor 方法源码解析
  • 三、总结

阅读此需阅读下面这些博客先
【Spring源码分析】Bean的元数据和一些Spring的工具
【Spring源码分析】BeanFactory系列接口解读
【Spring源码分析】执行流程之非懒加载单例Bean的实例化逻辑
【Spring源码分析】从源码角度去熟悉依赖注入(一)
【Spring源码分析】从源码角度去熟悉依赖注入(二)
【Spring源码分析】@Resource注入的源码解析
【Spring源码分析】循环依赖的底层源码剖析

Spring 在推断构造方法的逻辑上面写的实在太复杂了,构造种类贼多贼多,恕我实力有限,这里的话算不上源码分析,只能说是原理吧,这我只考虑了平常开发会遇到的,什么getBean去添加参数的、BeanDefinition去填充的我都一概跳过,看不懂一点(比如多参构造参数相同的情况下(@Autowired注解修饰后的required为false)会进行评分操作,有点复杂,感觉遇不着就不想研究了)。

(这篇博客可能有点水了)

在这里插入图片描述

一、确认候选构造——AutowireAnnotationBeanPostProcessor#determineCandidateConstructors

确认候选的构造也是通过 BeanPostProcessor 来进行的(SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors),引进的代码如下:

		// Candidate constructors for autowiring?// 提供一个扩展点,可以利用SmartInstantiationAwareBeanPostProcessor来控制用beanClass中的哪些构造方法// 比如AutowiredAnnotationBeanPostProcessor会把加了@Autowired注解的构造方法找出来,具体看代码实现会更复杂一点Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);@Nullableprotected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName)throws BeansException {if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {Constructor<?>[] ctors = bp.determineCandidateConstructors(beanClass, beanName);if (ctors != null) {return ctors;}}}return null;}

Spring 实现的 SmartInstantiationAwareBeanPostProcessor 的 determineCandidateConstructors 方法中就一个对其进行实现了——AutowireAnnotationBeanPostProcessor。

下面俺对其核心代码进行解析:

	@Override@Nullablepublic Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName)throws BeanCreationException {// Quick check on the concurrent map first, with minimal locking.Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);if (candidateConstructors == null) {// Fully synchronized resolution now...synchronized (this.candidateConstructorsCache) {candidateConstructors = this.candidateConstructorsCache.get(beanClass);if (candidateConstructors == null) {Constructor<?>[] rawCandidates;try {// 拿到所有的构造方法rawCandidates = beanClass.getDeclaredConstructors();} catch (Throwable ex) {throw new BeanCreationException(beanName,"Resolution of declared constructors on bean Class [" + beanClass.getName() +"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);}List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length);// 用来记录required为true的构造方法,一个类中只能有一个required为true的构造方法Constructor<?> requiredConstructor = null;// 用来记录默认无参的构造方法Constructor<?> defaultConstructor = null;// 遍历每个构造方法for (Constructor<?> candidate : rawCandidates) {// 当前遍历的构造方法是否写了@AutowiredMergedAnnotation<?> ann = findAutowiredAnnotation(candidate);// 当前构造方法上加了@Autowiredif (ann != null) {// 整个类中如果有一个required为true的构造方法,那就不能有其他的加了@Autowired的构造方法// 若存在required为true的还存在其他的@Autowired注解就抛出异常if (requiredConstructor != null) {throw new BeanCreationException(beanName,"Invalid autowire-marked constructor: " + candidate +". Found constructor with 'required' Autowired annotation already: " +requiredConstructor);}boolean required = determineRequiredStatus(ann);if (required) {if (!candidates.isEmpty()) {throw new BeanCreationException(beanName,"Invalid autowire-marked constructors: " + candidates +". Found constructor with 'required' Autowired annotation: " +candidate);}// 记录唯一一个required为true的构造方法requiredConstructor = candidate;}// 记录所有加了@Autowired的构造方法,不管required是true还是false// 如果默认无参的构造方法上也加了@Autowired,那么也会加到candidates中candidates.add(candidate);// 从上面代码可以得到一个结论,在一个类中,要么只能有一个required为true的构造方法,要么只能有一个或多个required为false的方法} else if (candidate.getParameterCount() == 0) {// 记录唯一一个无参的构造方法defaultConstructor = candidate;}// 有可能存在有参、并且没有添加@Autowired的构造方法}if (!candidates.isEmpty()) {// Add default constructor to list of optional constructors, as fallback.// 如果不存在一个required为true的构造方法,则所有required为false的构造方法和无参构造方法都是合格的if (requiredConstructor == null) {if (defaultConstructor != null) {candidates.add(defaultConstructor);} else if (candidates.size() == 1 && logger.isInfoEnabled()) {logger.info("Inconsistent constructor declaration on bean with name '" + beanName +"': single autowire-marked constructor flagged as optional - " +"this constructor is effectively required since there is no " +"default constructor to fall back to: " + candidates.get(0));}}// 如果只存在一个required为true的构造方法,那就只有这一个是合格的candidateConstructors = candidates.toArray(new Constructor<?>[0]);}// 没有添加了@Autowired注解的构造方法,并且类中只有一个构造方法,并且是有参的else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {candidateConstructors = new Constructor<?>[]{rawCandidates[0]};}else {// 如果有多个有参、并且没有添加@Autowired的构造方法,是会返回空的candidateConstructors = new Constructor<?>[0];}this.candidateConstructorsCache.put(beanClass, candidateConstructors);}}}return (candidateConstructors.length > 0 ? candidateConstructors : null);}

在这里插入图片描述
就是说只有三种情况有可用的构造(这里是指会有返回的候选构造方法):

  • 使用了 @Autowired 注解,有且只有一个 required 为 true;
  • 使用了 @Autowired 注解,required 都为 false;
  • 没有使用 @Autowired 注解,有且只有一个多参构造。

在这里插入图片描述通过这些代码的解析可以知道:

  • 含有多个参数多个构造和只有一个无参的都会走 instantiateBean 的逻辑,去通过无参构造去实例化,如果没有的话就抛异常咯,换句话说就是没用@Autowired注解就是去找无参,没有就抛异常
    • 在这里插入图片描述
    • 在这里插入图片描述
  • 有且只有一个 @Autowired 注解且required为true,有多个 @Autowired 注解,但是 required 都为 false,那么就是通过 autowireConstructor 方法去实现 Spring 的自动构造。

二、autowireConstructor 方法源码解析

太难了,看不了一点,直接阐述源码流程(和上面的接着的)。

上面确认候选构造的时候还有三种情况我们需要考虑:

  • 使用了 @Autowired 注解,有且只有一个 required 为 true;
  • 使用了 @Autowired 注解,required 都为 false;
  • 没有使用 @Autowired 注解,有且只有一个多参构造。

也就是上面这三种情况会进入到 autowireConstructor 方法。
下面阐述一下流程:

  1. 首先是对构造进行个排序,以参数的个数降序排序,就是参数个数多的在前面:
    • 在这里插入图片描述
  2. 然后去调用 beanFactory#resolveDependency 方法去给参数进行填充参数;
  3. 如果根据当前构造方法找到了对应的构造方法参数值,那么这个构造方法就是可用的,但是不一定这个构造方法就是最佳的,所以这里会涉及到是否有多个构造方法匹配了同样的值,这个时候就会用值和构造方法类型进行匹配程度的打分,找到一个最匹配的
    • 在这里插入图片描述
    • 在这里插入图片描述
      也就是说构造方法要满足可用的话,必须所有的参数都是Spring容器中拥有的,可以注入的。

三、总结

本篇并不是Spring所有推断构造方法的逻辑,只是其中的一部分——AutowireAnnotationBeanPostProcessor#determineCandidateConstructors 中推断出来的构造方法是怎么处理的。

主要就是有无 @Autowired 注解俩各方面。

在这里插入图片描述当有多个构造 @Autowired 注解,但是 required 都为 false 的话,考虑 autowireConstructor 的逻辑。先是将所候选的构造参数个数降序排序,然后逐个去匹配,看看参数是否都能在容器中找到,找不到就换下一个,找到就用该构造。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/250342.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java程序设计】【C00207】基于(JavaWeb+SSM)的宠物领养管理系统(论文+PPT)

基于&#xff08;JavaWebSSM&#xff09;的宠物领养管理系统&#xff08;论文PPT&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于ssm的宠物领养系统 本系统分为前台系统、管理员、收养者和寄养者4个功能模块。 前台系统&#xff1a;游客打开系统…

机器学习入门-----sklearn

机器学习基础了解 概念 机器学习是人工智能的一个实现途径 深度学习是机器学习的一个方法发展而来 定义:从数据中自动分析获得模型,并利用模型对特征数据【数据集:特征值+目标值构成】进行预测 算法 数据集的目标值是类别的话叫做分类问题;目标值是连续的数值的话叫做回…

FullStack之Django(1)开发环境配置

FullStack之Django(1)开发环境配置 author: Once Day date&#xff1a;2022年2月11日/2024年1月27日 漫漫长路&#xff0c;才刚刚开始… 全系列文档请查看专栏: FullStack开发_Once_day的博客-CSDN博客Django开发_Once_day的博客-CSDN博客 具体参考文档: The web framewor…

Docker中配置MySql环境

目录 一、简单安装 1. 首先从Docker Hub中拉取镜像 2. 启动尝试创建MySQL容器&#xff0c;并设置挂载卷。 3. 查看mysql8这个容器是否启动成功 4. 如果已经成功启动&#xff0c;进入容器中简单测试 4.1 进入容器 4.2 登录mysql中 4.3 进行简单添加查找测试 二、主从复…

C++函数分文件编写之VScode版

VScode实现函数的分文件编写 1.下载插件创建项目2.分文件编写内容3.修改主函数文件名 我在分享内容时经常用的软件是VScode&#xff0c;相信有些内存敏感或需要VScode便利性的小伙伴也是更愿意使用VScode。那么接下来我们就盘一盘怎样使用VScode实现分文件编写。 1.下载插件创建…

20240202在WIN10下使用fast whisper缺少cudnn_ops_infer64_8.dll

20240202在WIN10下使用fast whisper缺少cudnn_ops_infer64_8.dll 2024/2/2 10:48 https://blog.csdn.net/feinifi/article/details/132548556 Could not locate cudnn_ops_infer64_8.dll. Please make sure it is in your library path!解决办法 安装cuDNN c:\faster-whisper-…

用HTML5 + JavaScript实现下雪效果

用HTML5 JavaScript实现下雪效果 下面是用HTML5 JavaScript实现下雪效果示例&#xff0c;展示了如何使用 HTML5 的 <canvas> 元素以及 JavaScript 来创建下雪效果。效果如下&#xff1a; 源码如下&#xff1a; <!DOCTYPE html> <html lang"en">…

【Vue3】源码探索之旅:compiler-core之parseChildren函数(二)

简言 parseChildren函数是在baseParse函数中作为createRoot函数的子节点参数传入的&#xff0c;今天来探索下parseChildren函数。 parseChildren在 compiler-core/src/parse.ts文件内。 parseChildren 这个函数就是用来解析模板字符串内容的 &#xff0c;里面有个while循环…

vue全家桶之状态管理Vuex

一、认识应用状态管理 1.什么是状态管理 在开发中&#xff0c;我们会的应用程序需要处理各种各样的数据&#xff0c;这些数据需要保存在我们应用程序中的某一个位置&#xff0c;对于这些数据的管理我们就称之为是 状态管理。 在前面我们是如何管理自己的状态呢&#xff1f; …

HiSilicon352 android9.0 开机视频调试分析

一&#xff0c;开机视频概念 开机广告是在系统开机后实现播放视频功能。 海思Android解决方案在原生Android基础上&#xff0c;增加了开机视频模块&#xff0c;可在开机过程中播放视频文件&#xff0c;使用户更好的体验系统开机过程。 二&#xff0c;模块结构 1. 海思自研开机…

【Docker篇】Linux安装Docker、docker安装mysql、redis、rabbitmq

1.Linux安装docker 官方帮助文档&#xff1a;Install Docker Engine on CentOS | Docker Docs 1.1安装命令 # 1. 卸载之前的dockersudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate…

认识 SYN Flood 攻击

文章目录 1.什么是 SYN Flood 攻击&#xff1f;2.半连接与全连接队列3.如何防范 SYN Flood 攻击&#xff1f;增大半连接队列开启 SYN Cookie减少 SYNACK 重传次数 参考文献 1.什么是 SYN Flood 攻击&#xff1f; SYN Flood 是互联网上最原始、最经典的 DDoS&#xff08;Distri…

dvwa,xss反射型lowmedium

xss&#xff0c;反射型&#xff0c;low&&medium low发现xss本地搭建实操 medium作为初学者的我第一次接触比较浅的绕过思路high low 发现xss 本关无过滤 <script>alert(/xss/)</script> //或 <script>confirm(/xss/)</script> //或 <scr…

2024数学建模美赛F题思路代码分享

非法的野生动物贸易会对我们的环境产生负面影响&#xff0c;并威胁到全球的生物多样性。据估 计&#xff0c;它每年涉及高达265亿美元&#xff0c;被认为是全球第四大非法交易。[1]你将开发一个由数 据驱动的5年项目&#xff0c;旨在显著减少非法野生动物贸易。你的目标是说服一…

【24美赛思路已出】2024年美赛A~F题解题思路已出 | 无偿自提

A题&#xff1a;资源可用性和性别比例 问题一&#xff1a; 涉及当灯鱼种群的性别比例发生变化时&#xff0c;对更大的生态系统产生的影响。为了分析这个问题&#xff0c;可以采用以下的数学建模思路&#xff1a;建立灯鱼种群模型&#xff1a; 首先&#xff0c;建立一个灯鱼种群…

C语言第十六弹---操作符(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 操作符 1、下标访问[]、函数调用() 1.1、[ ] 下标引用操作符 1.2、函数调用操作符 2、结构成员访问操作符 2.1、结构体 2.1.1、结构的声明 2.1.2、结构体变…

计算机网络第4章(网络层)

4.1、网络层概述 简介 网络层的主要任务是实现网络互连&#xff0c;进而实现数据包在各网络之间的传输 这些异构型网络N1~N7如果只是需要各自内部通信&#xff0c;他们只要实现各自的物理层和数据链路层即可 但是如果要将这些异构型网络互连起来&#xff0c;形成一个更大的互…

【Python笔记-设计模式】建造者模式

一、说明 又称生成器&#xff0c;是一种创建型设计模式&#xff0c;使其能够分步骤创建复杂对象。允许使用相同的创建代码生成不同类型和形式的对象。 (一) 解决问题 对象的创建问题&#xff1a;当一个对象的构建过程复杂&#xff0c;且部分构建过程相互独立时&#xff0c;可…

基于SpringBoot Vue单位考勤管理系统

大家好✌&#xff01;我是Dwzun。很高兴你能来阅读我&#xff0c;我会陆续更新Java后端、前端、数据库、项目案例等相关知识点总结&#xff0c;还为大家分享优质的实战项目&#xff0c;本人在Java项目开发领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#x…

C#——三角形面积公式

已知三角形的三个边&#xff0c;求面积&#xff0c;可以使用海伦公式。 因此&#xff0c;可以执行得到三角形面积公式的计算方法代码如下&#xff1a; /** / <summary>* / 三角形面积公式* / </summary>* / <param name"a">边长a</param>*…