java-spring 09 下.populateBean (方法成员变量的注入@Autowird,@Resource)
1.在populateBean 方法中的一部分:用于@Autowird,@Resource注入
// 后处理器已经初始化boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();// 需要依赖检查boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);PropertyDescriptor[] filteredPds = null;if (hasInstAwareBpps) {if (pvs == null) {pvs = mbd.getPropertyValues();}// 3. 成员变量的注入// 调用了InstantiationAwareBeanPostProcessor.postProcessProperties 方法 和 postProcessPropertyValues 方法 来进行设值后处理for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; // 调用设值PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}// 如果postProcessProperties 返回null,再调用 postProcessPropertyValues这个过时的方法pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}}pvs = pvsToUse;}}}
2.关键方法ibp.postProcessProperties方法:
Spring在AutowiredAnnotationBeanPostProcessor的postProcessProperties()方法中,会遍历所找到的注入点依次进行注入。
@Overridepublic PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {// 找注入点(所有被@Autowired注解了的Field或Method)InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);}catch (BeanCreationException ex) {throw ex;}catch (Throwable ex) {throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);}return pvs;}
2.1 findAutowiringMetadata方法:
属性元数据封装到AutowiredFieldElement,方法元数据封装到AutowiredMethodElement,最后都封装到InjectionMetadata
`
二者都是AutowiredAnnotationBeanPostProcessor的内部类,继承InjectionMetadata.InjectedElement

metadata.inject方法:
循环遍历属性、方法元数据进行注入;封装的对象都是继承InjectedElement的
·
会先进行属性注入,再进行方法注入,所以属性注入会存在被覆盖的可能
·
根据这个特性,可以对static修饰的对象,进行方法注入,达到属性注入的效果
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> elementsToIterate =(checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {// 遍历每个注入点进行依赖注入for (InjectedElement element : elementsToIterate) {element.inject(target, beanName, pvs);}}}
element.inject方法:
这个方法会在AutowiredFieldElement和AutowiredMethodElement重写
AutowiredFieldElement.inject方法:
@Overrideprotected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Field field = (Field) this.member;Object value;if (this.cached) {// 对于原型Bean,第一次创建的时候,也找注入点,然后进行注入,此时cached为false,注入完了之后cached为true// 第二次创建的时候,先找注入点(此时会拿到缓存好的注入点),也就是AutowiredFieldElement对象,此时cache为true,也就进到此处了// 注入点内并没有缓存被注入的具体Bean对象,而是beanName,这样就能保证注入到不同的原型Bean对象try {value = resolvedCachedArgument(beanName, this.cachedFieldValue);}catch (NoSuchBeanDefinitionException ex) {// Unexpected removal of target bean for cached argument -> re-resolvevalue = resolveFieldValue(field, bean, beanName);}}else {// 根据filed从BeanFactory中查到的匹配的Bean对象value = resolveFieldValue(field, bean, beanName);}// 反射给filed赋值if (value != null) {ReflectionUtils.makeAccessible(field);field.set(bean, value);}}
resolveFieldValue方法:resolveDependency方法来获取依赖的属性bean
@Nullableprivate Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {DependencyDescriptor desc = new DependencyDescriptor(field, this.required);desc.setContainingClass(bean.getClass());Set<String> autowiredBeanNames = new LinkedHashSet<>(1);Assert.state(beanFactory != null, "No BeanFactory available");TypeConverter typeConverter = beanFactory.getTypeConverter();Object value;try {value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);}catch (BeansException ex) {throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);}synchronized (this) {if (!this.cached) {Object cachedFieldValue = null;if (value != null || this.required) {cachedFieldValue = desc;// 注册一下beanName依赖了autowiredBeanNames,registerDependentBeans(beanName, autowiredBeanNames);if (autowiredBeanNames.size() == 1) {String autowiredBeanName = autowiredBeanNames.iterator().next();if (beanFactory.containsBean(autowiredBeanName) &&beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {// 构造一个ShortcutDependencyDescriptor作为缓存,保存了当前filed所匹配的autowiredBeanName,而不是对应的bean对象(考虑原型bean)cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName, field.getType());}}}this.cachedFieldValue = cachedFieldValue;this.cached = true;}}return value;}}
AutowiredMethodElement.inject方法:
@Overrideprotected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {// 如果pvs中已经有当前注入点的值了,则跳过注入if (checkPropertySkipping(pvs)) {return;}Method method = (Method) this.member;Object[] arguments;if (this.cached) {try {arguments = resolveCachedArguments(beanName);}catch (NoSuchBeanDefinitionException ex) {// Unexpected removal of target bean for cached argument -> re-resolvearguments = resolveMethodArguments(method, bean, beanName);}}else {arguments = resolveMethodArguments(method, bean, beanName);}if (arguments != null) {try {ReflectionUtils.makeAccessible(method);method.invoke(bean, arguments);}catch (InvocationTargetException ex) {throw ex.getTargetException();}}}
resolveMethodArguments方法:
@Nullableprivate Object[] resolveMethodArguments(Method method, Object bean, @Nullable String beanName) {int argumentCount = method.getParameterCount();Object[] arguments = new Object[argumentCount];DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);Assert.state(beanFactory != null, "No BeanFactory available");TypeConverter typeConverter = beanFactory.getTypeConverter();// 遍历每个方法参数,找到匹配的bean对象for (int i = 0; i < arguments.length; i++) {MethodParameter methodParam = new MethodParameter(method, i);DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);currDesc.setContainingClass(bean.getClass());descriptors[i] = currDesc;try {Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);if (arg == null && !this.required) {arguments = null;break;}arguments[i] = arg;}catch (BeansException ex) {throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);}}synchronized (this) {if (!this.cached) {if (arguments != null) {DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);registerDependentBeans(beanName, autowiredBeans);if (autowiredBeans.size() == argumentCount) {Iterator<String> it = autowiredBeans.iterator();Class<?>[] paramTypes = method.getParameterTypes();for (int i = 0; i < paramTypes.length; i++) {String autowiredBeanName = it.next();if (beanFactory.containsBean(autowiredBeanName) &&beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {cachedMethodArguments[i] = new ShortcutDependencyDescriptor(descriptors[i], autowiredBeanName, paramTypes[i]);}}}this.cachedMethodArguments = cachedMethodArguments;}else {this.cachedMethodArguments = null;}this.cached = true;}}return arguments;}
字段注入
- 遍历所有的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对象了,不用再次进行查找了
- 利用反射将找到的所有结果对象传给当前方法,并执行。
相关文章:
java-spring 09 下.populateBean (方法成员变量的注入@Autowird,@Resource)
1.在populateBean 方法中的一部分:用于Autowird,Resource注入 // 后处理器已经初始化boolean hasInstAwareBpps hasInstantiationAwareBeanPostProcessors();// 需要依赖检查boolean needsDepCheck (mbd.getDependencyCheck() ! AbstractBeanDefinitio…...
赛氪网携手众机构助力第七届京津冀生态修复实践论坛圆满落幕
近日,由北京生态修复学会联合工业固废网、中国老科协国土资源分会共同主办,赛氪网作为支持单位的第七届京津冀生态修复实践论坛在北京温德姆酒店圆满落幕。本次论坛汇聚了众多行业专家、学者以及企业代表,共同探讨生态修复领域的新技术、新方…...
Naive RAG 、Advanced RAG 和 Modular RAG 简介
简介: RAG(Retrieval-Augmented Generation)系统是一种结合了检索(Retrieval)和生成(Generation)的机制,用于提高大型语言模型(LLMs)在特定任务上的表现。随…...
Python高级编程-DJango2
Python高级编程-DJango2 没有清醒的头脑,再快的脚步也会走歪;没有谨慎的步伐,再平的道路也会跌倒。 目录 Python高级编程-DJango2 1.显示基本网页 2.输入框的形式: 1)文本输入框 2)单选框 3ÿ…...
bash脚本 报错:/bin/bash^M:解释器错误: 没有那个文件或目录
bash脚本 报错:/bin/bash^M:解释器错误: 没有那个文件或目录 出现这个问题是因为该脚本文件在windows下编辑过 在windows下,每一行的结尾是\n\r,而在linux下文件的结尾是\n,那么你在windows下编辑过的文件在linux下打…...
win10专业版远程桌面连接不上,win10专业版远程桌面连接不上常见原因与解决方法
Win10专业版远程桌面连接功能是一项非常实用的工具,它允许用户远程访问和操作另一台计算机。然而,有时在尝试进行远程桌面连接时,可能会遇到连接不上的情况。本文将分析导致这一问题的常见原因,并提供相应的解决方法。 一、常见原…...
前端 日期 new Date 少0 转换成 yyyy-MM-dd js vue
在console控制台直接输出new Date(),是这样: Fri May 10 2024 23:36:06 GMT0800 (中国标准时间) 输出new Date().toLocaleString(),是这样: 2024/5/10 23:36:06 输出new Date().toISOString(),是这样: …...
Linux中的磁盘分析工具ncdu
2024年5月14日,周二上午 概述 ncdu 是一个基于文本的用户界面磁盘使用情况分析工具。它可以在终端中快速扫描目录,并统计该目录下的文件和文件夹的磁盘使用情况,以交互友好的方式呈现给用户。 安装 在 Debian/Ubuntu 系统下,可…...
Angular入门
Angular版本:Angular 版本演进史概述-天翼云开发者社区 - 天翼云 安装nodejs:Node.js安装与配置环境 v20.13.1(LTS)-CSDN博客 Angular CLI是啥 Angular CLI 是一个命令行接口(Angular Command Line Interface),是开发 Angular 应用的最快、最…...
Java进阶11 IO流、功能流
Java进阶11 IO流-功能流 一、字符缓冲流 字符缓冲流在源代码中内置了字符数组,可以提高读写效率 1、构造方法 方法说明BufferedReader(new FileReader(文件路径))对传入的字符输入流进行包装BufferedWriter(new FileWriter(文件路径))对传入的字符输出流进行包装…...
windows 安装 Conda
1 Conda简介 Conda 是一个开源的软件包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换。Conda 是为 Python 程序创建的,适用于 Linux,OS X 和Windows,也可以打包和分发其他软件。一般用conda来维护多个python版本。 2 安装…...
IPsec VPN简介
什么是IPsec? IPsec(Internet Protocol Security)是为IP网络提供安全性的协议和服务的集合,它是VPN(Virtual Private Network,虚拟专用网)中常用的一种技术。其实就是一种协议簇(类…...
探索 Canva 的功能以及如何有效使用 Canva
『创意瞬间变现!Canva AI Drawing 让你的文字描绘成艺术』 在数字设计和创意领域,Canva 是创新和用户友好性的灯塔。这个平台不仅简化了图形设计,还引入了 AI Drawing 等强大工具,使其成为专业人士和初学者的首选解决方案。让我们…...
python中匿名函数简单样例
目录 一、匿名函数(也称为 lambda 函数): 二、简单样例: 2.1 filter() 函数: 2.2 map() 函数: 2.3 sorted() 函数: 一、匿名函数(也称为 lambda 函数): 简洁性:匿名函数通常比命…...
【SpringBoot】 什么是springboot(二)?springboot操作mybatisPlus、swagger、thymeleaf模板
文章目录 SpringBoot第三章1、整合mybatsPlus1-234-67-10问题 2、整合pageHelper分页3、MP代码生成器1、编写yml文件2、导入依赖3、创建mp代码生成器4、生成代码5、编写配置类扫描mapper类6、编写控制器类 4、swagger1、什么是swagger2、作用3、发展历程4、一个简单的swagger项…...
【JavaWeb】前后端分离SpringBoot项目快速排错指南
1 发起业务请求 打开浏览器开发者工具,同时显示网络(Internet)和控制台(console) 接着,清空控制台和网络的内容,如下图 然后,点击你的业务按钮,发起请求。 首先看控制台…...
Go语言高级特性
目录 1. 并发编程 1.1 Goroutine轻量级线程 1.2 Channel通信机制 1.3 WaitGroup等待组 1.4 Mutex互斥锁 2. 垃圾回收机制 2.1 内存管理介绍 2.2 垃圾回收原理 2.3 性能调优策略 2.4 常见问题及解决方案 3. 接口与反射 3.1 接口定义与实现 3.2 空接口与类型断言 3…...
边缘计算安全有多重要
德迅云安全研究发现边缘安全是对存储或处理在网络边缘的数据的保护。边缘可以用不同的方式定义,但一般来说,它包括企业直接控制之外的任何设备或位置。这可能包括传感器、连接物联网的设备和移动设备。 边缘计算正在彻底改变商业运作方式。这引发了对边缘…...
Uniapp开发入门:构建跨平台应用的全面指南
引言 什么是Uniapp Uniapp是一款由DCloud公司推出的基于Vue.js的跨平台应用开发框架。它的核心理念是“一套代码,多端运行”,开发者只需编写一份代码,即可生成包括iOS、Android、H5、微信小程序、支付宝小程序、百度小程序等多平台的应用。…...
初级银行从业资格证知识点(十)
中国银保监会近年来启动了银行业保险业清廉金融文化建设活动,旨在通过全覆盖参与、全过程融入、全方位提升,增强金融从业人员清廉从业意识,培育清廉金融理念,通过文化的渗透力和影响力,厚植清廉根基,提升金…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...
