【Spring】Spring之依赖注入源码解析
1 Spring注入方式
1.1 手动注入
xml中定义Bean,程序员手动给某个属性赋值。
- set方式注入
<bean name="userService" class="com.firechou.service.UserService"><property name="orderService" ref="orderService"/>
</bean>
- 构造方法注入
<bean name="userService" class="com.firechou.service.UserService"><constructor-arg index="0" ref="orderService"/>
</bean>
1.2 自动注入
1.2.1 XML的autowire自动注入
<bean id="userService" class="com.firechou.service.UserService" autowire="byType"/>
Spring会自动的给userService中所有的属性自动赋值(不需要这个属性上有@Autowired注解,但需要这个属性有对应的set方法);
XML的自动注入底层其实也就是set方法注入和构造方法注入;
Bean的自动注入模式autowire:
- byType
获取到set方法中的唯一参数的参数类型,并且根据该类型去容器中获取bean,如果找到多个,会报错。
- byName
找到所有set方法所对应的XXX部分的名字,根据XXX部分的名字去获取bean。
- constructor
通过构造方法注入,可以不写set方法;
如果存在多个构造方法,则通过构造方法推断来选择某个构造方法;
Spring利用构造方法的参数信息从Spring容器中去找Bean,找到Bean之后作为参数传给构造方法,从而实例化得到一个bean对象,并完成属性赋值(属性赋值的代码得程序员来写);
- default
- no
关闭autowire
PropertyDescriptor:
创建Bean的过程中,填充属性时,Spring会去解析当前类,解析类中每个方法得到对应的PropertyDescriptor对象,PropertyDescriptor中有几个属性:
- name:这个name并不是方法的名字,而是拿方法名字进过处理后的名字
- 如果方法名字以“get”开头,比如“getXXX”,那么name=XXX
- 如果方法名字以“is”开头,比如“isXXX”,那么name=XXX
- 如果方法名字以“set”开头,比如“setXXX”,那么name=XXX
- readMethodRef:表示get方法的Method对象的引用
- readMethodName:表示get方法的名字
- writeMethodRef:表示set方法的Method对象的引用
- writeMethodName:表示set方法的名字
- propertyTypeRef:如果有get方法那么对应的就是返回值的类型,如果是set方法那么对应的就是set方法中唯一参数的类型
什么是set和get方法:
- get方法定义
方法参数个数为0个,并且方法名字以"get"开头,或者方法名字以"is"开头并且方法的返回类型为boolean
- set方法定义
方法参数个数为1个,并且方法名字以"set"开头并且方法返回类型为void
1.2.2 @Autowired注解的自动注入
@Autowired注解相当于XML中的autowire属性的注解方式的替代;
@Autowired注解提供了与XML中autowire相同的功能,但是拥有更细粒度的控制和更广泛的适用性;
XML中的autowire控制的是整个bean的所有属性,而@Autowired注解是直接写在某个属性、某个set方法、某个构造方法上的;
如果一个类有多个构造方法,那么如果用XML的autowire=constructor,无法控制到底用哪个构造方法,可以用@Autowired注解来直接指定想用哪个构造方法;
@Autowired注解还可以控制,哪些属性想被自动注入,哪些属性不想,这也是细粒度的控制;
@Autowired无法区分byType和byName,@Autowired是先byType,如果找到多个则byName;
@Autowired注解可以写在:
- 属性上:先根据属性类型去找Bean,如果找到多个再根据属性名确定一个
- 构造方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
- set方法上:先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
2 寻找注入点
在创建一个Bean的过程中,Spring会利用AutowiredAnnotationBeanPostProcessor的**postProcessMergedBeanDefinition()**找出注入点并缓存。
找注入点的流程为:
- 遍历当前类的所有的属性字段Field
- 查看字段上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该字段是一个注入点
- 如果字段是static的,则不进行注入
- 获取@Autowired中的required属性的值
- 将字段信息构造成一个AutowiredFieldElement对象,作为一个注入点对象添加到currElements集合中。
- 遍历当前类的所有方法Method
- 判断当前Method是否是桥接方法,如果是找到原方法
- 查看方法上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该方法是一个注入点
- 如果方法是static的,则不进行注入
- 获取@Autowired中的required属性的值
- 将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到currElements集合中
- 遍历完当前类的字段和方法后,将遍历父类的,直到没有父类
- 最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对于的注入点集合对象,并缓存
3 注入点进行注入
Spring在AutowiredAnnotationBeanPostProcessor的**postProcessProperties()**方法中,会遍历所找到的注入点依次进行注入。
字段注入:
- 遍历所有的AutowiredFieldElement对象。
- 将对应的字段封装为DependencyDescriptor对象。
- 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前字段所匹配的Bean对象。
- 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了,不用再次进行查找了
- 利用反射将结果对象赋值给字段。
Set方法注入:
- 遍历所有的AutowiredMethodElement对象
- 遍历将对应的方法的参数,将每个参数封装成MethodParameter对象
- 将MethodParameter对象封装为DependencyDescriptor对象
- 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前方法参数所匹配的Bean对象。
- 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了,不用再次进行查找了
- 利用反射将找到的所有结果对象传给当前方法,并执行。
相关文章:
【Spring】Spring之依赖注入源码解析
1 Spring注入方式 1.1 手动注入 xml中定义Bean,程序员手动给某个属性赋值。 set方式注入 <bean name"userService" class"com.firechou.service.UserService"><property name"orderService" ref"orderService"…...

【微软知识】微软相关技术知识分享
微软技术领域 一、微软操作系统: 微软的操作系统主要是 Windows 系列,包括 Windows 10、Windows Server 等。了解 Windows 操作系统的基本使用、配置和故障排除是非常重要的。微软操作系统(Microsoft System)是美国微软开发的Wi…...
12.python设计模式【观察者模式】
内容:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变的时候,所有依赖于它的对象得到通知并被自动更新。观者者模式又称为“发布-订阅”模式。比如天气预报,气象局分发气象数据。 角色: 抽象主题…...

重生之我要学C++第五天
这篇文章主要内容是构造函数的初始化列表以及运算符重载在顺序表中的简单应用,运算符重载实现自定义类型的流插入流提取。希望对大家有所帮助,点赞收藏评论,支持一下吧! 目录 构造函数进阶理解 1.内置类型成员在参数列表中的定义 …...

复习之linux高级存储管理
一、lvm----逻辑卷管理 1.lvm定义 LVM是 Logical Volume Manager(逻辑卷管理)的简写,它是Linux环境下对磁盘分区进行管理的一种机制。 逻辑卷管理器(LogicalVolumeManager)本质上是一个虚拟设备驱动,是在内核中块设备和物理设备…...

HuggingGPT Solving AI Tasks with ChatGPT and its Friends in Hugging Face
总述 HuggingGPT 让LLM发挥向路由器一样的作用,让LLM来选择调用那个专业的模型来执行任务。HuggingGPT搭建LLM和专业AI模型的桥梁。Language is a generic interface for LLMs to connect AI models 四个阶段 Task Planning: 将复杂的任务分解。但是这里…...
java工程重写jar包中class类覆盖问题
结论:直接在程序中复写jar中的类即可 原因:一般我java工程是运行在tomcat容器中,tomcat容易在加载我们工程类和jar包是的优先级为: 我们工程的class 先于 我们工程lib下的jar 重复的类只加载一次,加载我们复写后的类后…...
Mybatis基于注解与XML开发
文章目录 1 关于SpringBoot2 关于MyBatis2.1 MyBatis概述2.2 MyBatis核心思想2.3 MyBatis使用流程3 MyBatis配置SQL方式3.1 基于注解方式3.1.1 说明3.1.2 使用流程3.1.3 常用注解 3.2 基于XML方式3.2.1 相比注解优势3.2.2 使用流程3.2.3 常用标签 1 关于SpringBoot SpringBoot…...

数字化转型导师坚鹏:数字化时代扩大内需的8大具体建议
在日新月异的数字化时代、复杂多变的国际化环境下,扩大内需成为推动经济发展的国家战略,如何真正地扩大内需?结合本人15年的管理咨询经验及目前实际情况的深入研究,提出以下8大具体建议: 1、制定国民收入倍增计划。结…...

M1/M2 通过VM Fusion安装Win11 ARM,解决联网和文件传输
前言 最近新入了Macmini M2,但是以前的老电脑的虚拟机运行不起来了。😅,实际上用过K8S的时候,会发现部分镜像也跑不起来,X86的架构和ARM实际上还是有很多隐形兼容问题。所以只能重新安装ARM Win11,幸好微软…...
Linux中显示系统正在运行的进程的命令
2023年7月29日,周六上午 在Linux中,ps命令用于显示当前系统中正在运行的进程, ps应该是processes snapshot(进程快照)的缩写。 以下是ps命令的常见用法和示例: 显示当前用户的所有进程:ps 显示…...

vite中安装less
使用vite创建的项目,默认是没有安装less的 如果直接在style中书写less 会报下图错误: 解决方案: npm install --save less 在package.json中查看是否安装成功 安装完成刷新页面,问题解决...

Aduino中eps环境搭建
这里只记录Arduino2.0以后版本:如果有外网环境,那么可以轻松搜到ESP32开发板环境并安装,如果没有,那就见下面操作: 进入首选项,将esp8266的国内镜像地址填入,然后保存,在开发板中查…...

python——案例二 求两个数的和
#案例二 求两个数的和 num1input(请输入第一个数字:) num2input(请输入第二个数字:) sumfloat(num1)float(num2) #计算公式 print(sum) #显示结果 输入num11、num22得到结果sum3...

一文了解 Android 车机如何处理中控的旋钮输入?
前言 上篇文章《从实体按键看 Android 车载的自定义事件机制》带大家了解了 Android 车机支持自定义输入的机制 CustomInputService。事实上,除了支持自定义事件,对于中控上常见的音量控制、焦点控制的旋钮事件,Android 车机也是支持的。 那…...

小红书推广 方法总结
大家好,我是网媒智星,今天跟大家分享一下小红书的推广方法和经验。 一、平台简介 1、什么是小红书? 小红书是一个消费决策/生活方式平台,用户可以通过图片、文案、视频等方式分享美好生活。 2、用户画像 - 2亿月活跃…...

通讯录的实现(超详细)——C语言(进阶)
目录 一、创建联系人信息(结构体) 二、创建通讯录(结构体) 三、define定义常量 四、打印通讯录菜单 五、枚举菜单选项 六、初始化通讯录 七、实现通讯的的功能 7.1 增加加联系人 7.2 显示所有联系人的信息 7.3 单独查…...

3D 渲染技巧-如何创建高质量写实渲染?
掌握创建高质量建筑渲染和任何 3D 渲染的艺术是一项复杂且需要技巧的工作,通常需要多年的经验和实践。实现逼真的结果需要仔细考虑众多因素,并避免可能导致缺乏真实性的假渲染效果的常见错误。 避免常见错误 - 提升渲染游戏的技巧 在追求创建真正逼真的…...
fastadmin采坑之获取当前登录admin用户的信息
在controller层里想要获取当前登录admin用户的信息 print_r($this->auth->getUserInfo());但是有个问题 我在fa_admin表中添加了新的字段,这个方法获取不到新字段的数值,具体也没有去研究估计跟方法有关 然后我直接用模型去获取数据,简…...

【Spring AOP + 自定义注解 + 动态数据源 实现主从库切换读写分离】—— 案例实战
💧 S p r i n g A O P 主从数据源切换 读写分离 自定义注解案例实战! \color{#FF1493}{Spring AOP 主从数据源切换 读写分离 自定义注解 案例实战!} SpringAOP主从数据源切换读写分离自定义注解案例实战!💧 …...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...

Qt的学习(二)
1. 创建Hello Word 两种方式,实现helloworld: 1.通过图形化的方式,在界面上创建出一个控件,显示helloworld 2.通过纯代码的方式,通过编写代码,在界面上创建控件, 显示hello world; …...