当前位置: 首页 > news >正文

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;}

字段注入

  1. 遍历所有的AutowiredFieldElement对象。
  2. 将对应的字段封装为DependencyDescriptor对象。
  3. 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前字段所匹配的Bean对象。
  4. 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了,不用再次进行查找了
  5. 利用反射将结果对象赋值给字段。

Set方法注入

  1. 遍历所有的AutowiredMethodElement对象
  2. 遍历将对应的方法的参数,将每个参数封装成MethodParameter对象
  3. 将MethodParameter对象封装为DependencyDescriptor对象
  4. 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依赖查找,找到当前方法参数所匹配的Bean对象。
  5. 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象了,不用再次进行查找了
  6. 利用反射将找到的所有结果对象传给当前方法,并执行。

相关文章:

java-spring 09 下.populateBean (方法成员变量的注入@Autowird,@Resource)

1.在populateBean 方法中的一部分&#xff1a;用于Autowird&#xff0c;Resource注入 // 后处理器已经初始化boolean hasInstAwareBpps hasInstantiationAwareBeanPostProcessors();// 需要依赖检查boolean needsDepCheck (mbd.getDependencyCheck() ! AbstractBeanDefinitio…...

赛氪网携手众机构助力第七届京津冀生态修复实践论坛圆满落幕

近日&#xff0c;由北京生态修复学会联合工业固废网、中国老科协国土资源分会共同主办&#xff0c;赛氪网作为支持单位的第七届京津冀生态修复实践论坛在北京温德姆酒店圆满落幕。本次论坛汇聚了众多行业专家、学者以及企业代表&#xff0c;共同探讨生态修复领域的新技术、新方…...

Naive RAG 、Advanced RAG 和 Modular RAG 简介

简介&#xff1a; RAG&#xff08;Retrieval-Augmented Generation&#xff09;系统是一种结合了检索&#xff08;Retrieval&#xff09;和生成&#xff08;Generation&#xff09;的机制&#xff0c;用于提高大型语言模型&#xff08;LLMs&#xff09;在特定任务上的表现。随…...

Python高级编程-DJango2

Python高级编程-DJango2 没有清醒的头脑&#xff0c;再快的脚步也会走歪&#xff1b;没有谨慎的步伐&#xff0c;再平的道路也会跌倒。 目录 Python高级编程-DJango2 1.显示基本网页 2.输入框的形式&#xff1a; 1&#xff09;文本输入框 2&#xff09;单选框 3&#xff…...

bash脚本 报错:/bin/bash^M:解释器错误: 没有那个文件或目录

bash脚本 报错&#xff1a;/bin/bash^M&#xff1a;解释器错误: 没有那个文件或目录 出现这个问题是因为该脚本文件在windows下编辑过 在windows下&#xff0c;每一行的结尾是\n\r&#xff0c;而在linux下文件的结尾是\n&#xff0c;那么你在windows下编辑过的文件在linux下打…...

win10专业版远程桌面连接不上,win10专业版远程桌面连接不上常见原因与解决方法

Win10专业版远程桌面连接功能是一项非常实用的工具&#xff0c;它允许用户远程访问和操作另一台计算机。然而&#xff0c;有时在尝试进行远程桌面连接时&#xff0c;可能会遇到连接不上的情况。本文将分析导致这一问题的常见原因&#xff0c;并提供相应的解决方法。 一、常见原…...

前端 日期 new Date 少0 转换成 yyyy-MM-dd js vue

在console控制台直接输出new Date()&#xff0c;是这样&#xff1a; Fri May 10 2024 23:36:06 GMT0800 (中国标准时间) 输出new Date().toLocaleString()&#xff0c;是这样&#xff1a; 2024/5/10 23:36:06 输出new Date().toISOString()&#xff0c;是这样&#xff1a; …...

Linux中的磁盘分析工具ncdu

2024年5月14日&#xff0c;周二上午 概述 ncdu 是一个基于文本的用户界面磁盘使用情况分析工具。它可以在终端中快速扫描目录&#xff0c;并统计该目录下的文件和文件夹的磁盘使用情况&#xff0c;以交互友好的方式呈现给用户。 安装 在 Debian/Ubuntu 系统下&#xff0c;可…...

Angular入门

Angular版本&#xff1a;Angular 版本演进史概述-天翼云开发者社区 - 天翼云 安装nodejs&#xff1a;Node.js安装与配置环境 v20.13.1(LTS)-CSDN博客 Angular CLI是啥 Angular CLI 是一个命令行接口(Angular Command Line Interface)&#xff0c;是开发 Angular 应用的最快、最…...

Java进阶11 IO流、功能流

Java进阶11 IO流-功能流 一、字符缓冲流 字符缓冲流在源代码中内置了字符数组&#xff0c;可以提高读写效率 1、构造方法 方法说明BufferedReader(new FileReader(文件路径))对传入的字符输入流进行包装BufferedWriter(new FileWriter(文件路径))对传入的字符输出流进行包装…...

windows 安装 Conda

1 Conda简介 Conda 是一个开源的软件包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换。Conda 是为 Python 程序创建的,适用于 Linux,OS X 和Windows,也可以打包和分发其他软件。一般用conda来维护多个python版本。 2 安装…...

IPsec VPN简介

什么是IPsec&#xff1f; IPsec&#xff08;Internet Protocol Security&#xff09;是为IP网络提供安全性的协议和服务的集合&#xff0c;它是VPN&#xff08;Virtual Private Network&#xff0c;虚拟专用网&#xff09;中常用的一种技术。其实就是一种协议簇&#xff08;类…...

探索 Canva 的功能以及如何有效使用 Canva

『创意瞬间变现&#xff01;Canva AI Drawing 让你的文字描绘成艺术』 在数字设计和创意领域&#xff0c;Canva 是创新和用户友好性的灯塔。这个平台不仅简化了图形设计&#xff0c;还引入了 AI Drawing 等强大工具&#xff0c;使其成为专业人士和初学者的首选解决方案。让我们…...

python中匿名函数简单样例

目录 一、匿名函数&#xff08;也称为 lambda 函数&#xff09;&#xff1a; 二、简单样例&#xff1a; 2.1 filter() 函数: 2.2 map() 函数: 2.3 sorted() 函数: 一、匿名函数&#xff08;也称为 lambda 函数&#xff09;&#xff1a; 简洁性&#xff1a;匿名函数通常比命…...

【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 发起业务请求 打开浏览器开发者工具&#xff0c;同时显示网络&#xff08;Internet&#xff09;和控制台&#xff08;console&#xff09; 接着&#xff0c;清空控制台和网络的内容&#xff0c;如下图 然后&#xff0c;点击你的业务按钮&#xff0c;发起请求。 首先看控制台…...

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…...

边缘计算安全有多重要

德迅云安全研究发现边缘安全是对存储或处理在网络边缘的数据的保护。边缘可以用不同的方式定义&#xff0c;但一般来说&#xff0c;它包括企业直接控制之外的任何设备或位置。这可能包括传感器、连接物联网的设备和移动设备。 边缘计算正在彻底改变商业运作方式。这引发了对边缘…...

Uniapp开发入门:构建跨平台应用的全面指南

引言 什么是Uniapp Uniapp是一款由DCloud公司推出的基于Vue.js的跨平台应用开发框架。它的核心理念是“一套代码&#xff0c;多端运行”&#xff0c;开发者只需编写一份代码&#xff0c;即可生成包括iOS、Android、H5、微信小程序、支付宝小程序、百度小程序等多平台的应用。…...

初级银行从业资格证知识点(十)

中国银保监会近年来启动了银行业保险业清廉金融文化建设活动&#xff0c;旨在通过全覆盖参与、全过程融入、全方位提升&#xff0c;增强金融从业人员清廉从业意识&#xff0c;培育清廉金融理念&#xff0c;通过文化的渗透力和影响力&#xff0c;厚植清廉根基&#xff0c;提升金…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

基于Springboot+Vue的办公管理系统

角色&#xff1a; 管理员、员工 技术&#xff1a; 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能&#xff1a; 该办公管理系统是一个综合性的企业内部管理平台&#xff0c;旨在提升企业运营效率和员工管理水…...

离线语音识别方案分析

随着人工智能技术的不断发展&#xff0c;语音识别技术也得到了广泛的应用&#xff0c;从智能家居到车载系统&#xff0c;语音识别正在改变我们与设备的交互方式。尤其是离线语音识别&#xff0c;由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力&#xff0c;广…...

深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向

在人工智能技术呈指数级发展的当下&#xff0c;大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性&#xff0c;吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型&#xff0c;成为释放其巨大潜力的关键所在&…...