[spring源码]spring推断构造方法

前言

Spring中的一个bean,需要实例化得到一个对象,而实例化就需要用到构造方法。

一般情况下,一个类只有一个构造方法:

  1. 要么是无参的构造方法
  2. 要么是有参的构造方法

如果只有一个无参的构造方法,那么实例化就只能使用这个构造方法了。

如果只有一个有参的构造方法,那么实例化时能使用这个构造方法吗?要分情况讨论:

  1. 使用AnnotationConfigApplicationContext,会使用这个构造方法进行实例化,那么Spring会根据构造方法的参数信息去寻找bean,然后传给构造方法
  2. 使用ClassPathXmlApplicationContext,表示使用XML的方式来使用bean,要么在XML中指定构造方法的参数值(手动指定),要么配置autowire=constructor让Spring自动去寻找bean做为构造方法参数值。

上面是只有一个构造方法的情况,那么如果有多个构造方法呢?

又分为两种情况,多个构造方法中存不存在无参的构造方法。

分析:一个类存在多个构造方法,那么Spring进行实例化之前,该如何去确定到底用哪个构造方法呢?

  1. 如果开发者指定了想要使用的构造方法,那么就用这个构造方法
  2. 如果开发者没有指定想要使用的构造方法,则看开发者有没有让Spring自动去选择构造方法
  3. 如果开发者也没有让Spring自动去选择构造方法,则Spring利用无参构造方法,如果没有无参构造方法,则报错

针对第一点,开发者可以通过什么方式来指定使用哪个构造方法呢?

  1. xml中的<constructor-arg>标签,这个标签表示构造方法参数,所以可以根据这个确定想要使用的构造方法的参数个数,从而确定想要使用的构造方法
  2. 通过@Autowired注解,@Autowired注解可以写在构造方法上,所以哪个构造方法上写了@Autowired注解,表示开发者想使用哪个构造方法,当然,它和第一个方式的不同点是,通过xml的方式,我们直接指定了构造方法的参数值,而通过@Autowired注解的方式,需要Spring通过byType+byName的方式去找到符合条件的bean作为构造方法的参数值

再来看第二点,如果开发者没有指定想要使用的构造方法,则看开发者有没有让Spring自动去选择构造方法,对于这一点,只能用在ClassPathXmlApplicationContext,因为通过AnnotationConfigApplicationContext没有办法去指定某个bean可以自动去选择构造方法,而通过ClassPathXmlApplicationContext可以在xml中指定某个bean的autowire为constructor,虽然这个属性表示通过构造方法自动注入,所以需要自动的去选择一个构造方法进行自动注入,因为是构造方法,所以顺便是进行实例化。

当然,还有一种情况,就是多个构造方法上写了@Autowired注解,那么此时Spring会报错。

但是,因为@Autowired还有一个属性required,默认为ture,所以一个类中,只有能一个构造方法标注了@Autowired或@Autowired(required=true),有多个会报错。但是可以有多个@Autowired(required=false),这种情况下,需要Spring从这些构造方法中去自动选择一个构造方法。

总结

  1. 默认情况,用无参构造方法,或者只有一个构造方法就用那一个
  2. 程序员指定了构造方法入参值,通过getBean()或者BeanDefinition.getConstructorArgumentValues()指定,那就用所匹配的构造方法
  3. 程序员想让Spring自动选择构造方法以及构造方法的入参值, autowire="constructor"
  4. 程序员通过@Autowired注解指定了某个构造方法,但是希望Spring自动找该构造方法的入参值

源码思路(整体流程)

  1. 根据BeanDefinition加载类得到Class对象
  2. 如果BeanDefinition绑定了一个Supplier,那就调用Supplier的get方法得到一个对象并直接返回
  3. 如果BeanDefinition中存在factoryMethodName,那么就调用该工厂方法得到一个bean对象并返回,@Bean都会通过这里进行实例化
  4. 调用determineCandidateConstructors()方法推断哪些构造方法是可以用的
  5. 如果存在可用得构造方法,或者当前BeanDefinition的autowired是AUTOWIRE_CONSTRUCTOR,或者BeanDefinition中指定了构造方法参数值,或者创建Bean的时候指定了构造方法参数值,那么就调用autowireConstructor()方法自动构造一个对象
  6. 最后,如果不是上述情况,就根据无参的构造方法实例化一个对象

推断构造方法(determineCandidateConstructors方法)

源码点:

 不同情况总结图:

 没有@Autowired代码点:

除非只有一个有参构造方法返回该构造方法,否则返回null用默认的无参构造器

  有@Autowired代码点:

要么只能有一个required为true的构造方法,要么全为required为false的方法,否则抛异常

构造方法注入(autowireConstructor方法)

  1. 先检查是否指定了具体的构造方法和构造方法参数值,或者在BeanDefinition中缓存了具体的构造方法或构造方法参数值,如果存在那么则直接使用该构造方法进行实例化
  2. 如果没有确定的构造方法或构造方法参数值,那么

        a.如果没有确定的构造方法,那么则找出类中所有的构造方法

        b.如果只有一个无参构造方法,那么直接使用无参构造方法进行实例化

        c.如果有多个可用的构造方法或者当前Bean需要通过构造方法自动注入todo

        d.根据所指定的构造方法参数值,确定所需要的最少的构造方法参数值的个数

        e.对所有的构造方法进行排序,public的方法排在最前面,都是public参数个数多的在前面

        f.遍历每个构造方法,如果参数个数小于所要求的参数个数,则遍历下一个

        g. 如果不是调用getBean方法时所指定的构造方法参数值,根据方法参数类型和名字从BeanFactory中匹配Bean对象,和@Autowired逻辑一样,先byType再byName

        h.如果时调用getBean方法时所指定的构造方法参数值,就直接利用这些值

         i.如果根据当前构造方法找到了对应的构造方法参数值,那么这个构造方法就是可用的,但是不一定这个构造方法就是最佳的,所以这里会涉及到是否有多个构造方法匹配了同样的值,这个时候就会用值和构造方法类型进行匹配程度的打分,找到一个最匹配的

流程图: Spring推断构造方法底层执行流程| ProcessOn免费在线作图,在线流程图,在线思维导图

为什么分越少优先级越高?

主要是计算找到的bean和构造方法参数类型匹配程度有多高。

假设bean的类型为A,A的父类是B,B的父类是C,同时A实现了接口D

如果构造方法的参数类型为A,那么完全匹配,得分为0

如果构造方法的参数类型为B,那么得分为2

如果构造方法的参数类型为C,那么得分为4

如果构造方法的参数类型为D,那么得分为1

可以直接使用如下代码进行测试:

Object[] objects = new Object[]{new A()};// 0
System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{A.class}, objects));// 2
System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{B.class}, objects));// 4
System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{C.class}, objects));// 1
System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{D.class}, objects));

@Bean的情况

首先,Spring会把@Bean修饰的方法解析成BeanDefinition:

  1. 如果方法不是static的,那么解析出来的BeanDefinition中:

            a.factoryBeanName为AppConfig所对应的beanName,比如"appConfig"

            b.factoryMethodName为对应的方法名,比如"aService"

            c.factoryClass为AppConfig.class

  2. 如果方法是static的,那么解析出来的BeanDefinition中:

                a.factoryBeanName为null

                b.factoryMethodName为对应的方法名,比如"aService"

                c.factoryClass也为AppConfig.class

在由@Bean生成的BeanDefinition中,有一个重要的属性isFactoryMethodUnique,表示factoryMethod是不是唯一的,在普通情况下@Bean生成的BeanDefinition的isFactoryMethodUnique为true,但是如果出现了方法重载,那么就是特殊的情况,比如:

	@Beanpublic static AService aService(){return new AService();}@Beanpublic AService aService(BService bService){return new AService();}

虽然有两个@Bean,但是肯定只会生成一个aService的Bean,那么Spring在处理@Bean时,也只会生成一个aService的BeanDefinition,比如Spring先解析到第一个@Bean,会生成一个BeanDefinition,此时isFactoryMethodUnique为true,但是解析到第二个@Bean时,会判断出来beanDefinitionMap中已经存在一个aService的BeanDefinition了,那么会把之前的这个BeanDefinition的isFactoryMethodUnique修改为false,并且不会生成新的BeanDefinition了。

并且后续在根据BeanDefinition创建Bean时,会根据isFactoryMethodUnique来操作,如果为true,那就表示当前BeanDefinition只对应了一个方法,那也就是只能用这个方法来创建Bean了,但是如果isFactoryMethodUnique为false,那就表示当前BeanDefition对应了多个方法,需要和推断构造方法的逻辑一样,去选择用哪个方法来创建Bean。

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

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

相关文章

Mysql 5.7 grant 授权异常

一、Mysql 问题描述 1.检查 mysql.table_priv 表&#xff0c;查看表的所属 select * from mysql.table_priv where table_name‘’; 2.检查 mysql.user 表&#xff0c;查看用于与白名单 select * from mysql.user; 3.查看 授权用户的权限 show grants for user_name; 4.查看 被…

【Sublime Text】格式化Json和XML

无package control解决方案 删除文件中的package control这一行并保存 下载 下载中

【CSS】——基础入门常见操作

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;CSS引入 二&#xff1a;CSS对元素进行美化 1&#xff1a;style修饰 2&#xff1a;选…

Chrome(谷歌浏览器中文版)下载安装(Windows 11)

目录 Chrome_10_30工具下载安装 Chrome_10_30 工具 系统&#xff1a;Windows 11 下载 官网&#xff1a;https://chrome.google-zh.com/&#xff0c;点击立即下载 下载完成&#xff08;已经下过一遍所以点了取消&#xff09; 安装 解压&#xff0c;打开安装包 点击下一步…

js中怎么把excel和pdf文件转换成图片打包下载

index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>文件转图片工具</title><!-- 本…

【Java】异常处理见解,了解,进阶到熟练掌握

各位看官早安午安晚安呀 如果您觉得这篇文章对您有帮助的话 欢迎您一键三连&#xff0c;小编尽全力做到更好 欢迎您分享给更多人哦 大家好我们今天来学习Java面向对象的的抽象类和接口&#xff0c;我们大家庭已经来啦~ 目录 1.(throws和throw&#xff09;我们不管这个异常&…

【MySQL】——数据库恢复技术

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…

使用Django REST framework构建RESTful API

使用Django REST framework构建RESTful API Django REST framework简介 安装Django REST framework 创建Django项目 创建Django应用 配置Django项目 创建模型 迁移数据库 创建序列化器 创建视图 配置URL 配置全局URL 配置认证和权限 测试API 使用Postman测试API 分页 过滤和排序…

Straightforward Layer-wise Pruning for More Efficient Visual Adaptation

对于模型中冗余的参数&#xff0c;一个常见的方法是通过结构化剪枝方法减少参数容量。例如&#xff0c;基于幅度值和基于梯度的剪枝方法。尽管这些方法在传统训练上通用性&#xff0c;本文关注的PETL迁移有两个不可避免的问题&#xff1a; 显著增加了模型存储负担。由于不同的…

微服务架构面试内容整理-服务拆分的原则

服务拆分是微服务架构设计的关键步骤,以下是一些常见的拆分原则: 1. 单一职责原则 每个微服务应只负责一项特定的业务功能或领域,确保服务的简单性和易于理解。 2. 业务能力驱动 根据业务能力或功能进行拆分,确保每个服务能够独立实现特定的业务价值。<

022集——统计多条线的总长度(CAD—C#二次开发入门)

如下图所示&#xff0c;选择多条线并统计长度&#xff1a; c#中不包含直接获取curve曲线长度 属性&#xff0c;需用如下方法&#xff1a;curve.GetDistanceAtParameter(item.EndParam) 附部分代码如下&#xff1a; using Autodesk.AutoCAD.ApplicationServices; using Autode…

W5100S-EVB-Pico2评估板介绍

目录 1 简介 2 硬件资源 2.1 硬件规格 2.2 引脚定义 2.3 工作条件 3 参考资料 3.1 RP2350数据手册 3.2 W5100S数据手册 3.3 原理图 原理图 & 物料清单 & Gerber 文件 3.4 尺寸图&#xff08;单位&#xff1a;mm&#xff09; 3.5 参考例程 认证 CE FCC …

【Python】自然语言处理神器:NLTK库初探与文本处理案例

自然语言处理神器&#xff1a;NLTK库初探与文本处理案例 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;Python 的 NLTK&#xff08;Natural Language Toolkit&#xff0c;自然语言工具包&#xff09;被广泛认为是入门级和应用级的强大工具之一。无论是处理文本、…

从放弃985计科保研到秋招进大厂,我是怎么做的?【秋招经验分享】

从放弃985计科保研到秋招进大厂&#xff0c;我是怎么做的 先来介绍一下我自己&#xff0c;bg为985计算机本科&#xff0c;我在放弃了计科保研以后&#xff0c;成功拿到了美团等大厂研发岗的offer。目前秋招已经接近尾声了&#xff0c;我想记录一下这段时间的秋招感悟和经验&…

STM32F103C8T6学习笔记4--模拟旋转编码器的按键中断

1、实验内容 通过旋转编码器正反转来计数&#xff0c;由对应的GPIO产生中断。但是我在Proteus里面没有找到相关的EC11旋转编码器元件&#xff0c;暂时通过电路模拟的方式实现。 S1按下引脚PB0产生低电平信号&#xff0c;触发中断&#xff0c;计数值减一。 S2按下引脚PB1产生低…

宝顶白芽,慢生活的味觉盛宴

在快节奏的生活中&#xff0c;人们愈发向往那种悠然自得、返璞归真的生活方式。白茶&#xff0c;以其独特的韵味和清雅的风格&#xff0c;成为了现代人追求心灵宁静与生活品质的象征。而在众多白茶之中&#xff0c;竹叶青茶业出品的宝顶白芽以其甘甜醇爽的特质&#xff0c;成为…

23.智能停车计费系统(基于springboot和vue的Java项目)

目录 1.系统的受众说明 2 相关概念和技术介绍 2.1 JAVA技术介绍 2.2 SpringBoot框架 2.3B/S架构 2.4 MySQL数据库 3 系统需求分析 3.1 问题定义 3.2 可行性分析 3.3系统用例分析 3.4 系统流程分析 3.4.1 登录流程 3.4.2 添加信息流程 3.4.3 删除信息流程 4…

C语言学习,标准库<locale.h>

<locale.h> 是 C 标准库中的一个头文件&#xff0c; 提供了本地化&#xff08;Localization&#xff09;和国际化&#xff08;Internationalization&#xff09;的支持。它提供了一组函数和宏来设置或查询程序的本地化信息&#xff0c;例如日期、时间、货币、数字格式等。…

Chrome 130 版本开发者工具(DevTools)更新内容

Chrome 130 版本开发者工具&#xff08;DevTools&#xff09;更新内容 一、网络&#xff08;Network&#xff09;面板更新 1. 重新定义网络过滤器 网络面板获新增了一些过滤条件&#xff0c;这些过滤条件是根据反馈重新设计的&#xff0c;特定于类型的过滤条件保持不变&…

清华双臂机器人扩散大模型RDT:先预训练后微调,支持语言、图像、动作多种输入(1B参数)

前言 通过上文介绍的GR2&#xff0c;我们看到了视频生成模型在机器人训练中的应用 无独有偶&#xff0c;和GR2差不多一个时期出来的清华RDT&#xff0c;其模型架构便基于视频生成架构DiT改造而成(当然&#xff0c;该清华团队其实也在DiT之前推出了U-ViT&#xff0c;具体下文会…