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

Spring编程常见错误50例-Spring Bean依赖注入常见错误(下)

@Value没有注入预期的值

问题

对于@Value可以装配多种类型的数据:

  • 装配对象:
@Value("#{student}")
private Student student;@Bean
public Student student(){Student student = createStudent(1, "xie");return student;
}
  • 装配字符串:
@Value("我是字符串")
private String text;
  • 注入系统参数、环境变量或者配置文件中的值:
@Value("${ip}")
private String ip
  • 注入其他Bean属性:
@Value("#{student.name}")  // student是bean的ID
private String name;

但是使用该注解时遇到以下场景会出现问题:在控制器类中引用配置类中的属性时部分值返回错误

username=admin
password=pass
@RestController
@Slf4j
public class ValueTestController {@Value("${username}")private String username;@Value("${password}")private String password;@RequestMapping(path = "user", method = RequestMethod.GET)public String getUser(){return username + ","  + ", " + password;  // username返回的是运行这段程序的计算机用户名,password能正确返回};}

原因

从下面代码中可以看到@Value的工作分为三个核心步骤:

// DefaultListableBeanFactory#doResolveDependency
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {// ...// ①寻找@ValueObject value = getAutowireCandidateResolver().getSuggestedValue(descriptor);if (value != null) {// ②解析value值if (value instanceof String) {String strVal = resolveEmbeddedValue((String) value);BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);value = evaluateBeanDefinitionString(strVal, bd);}// ③转化Value解析的结果到装配的类型TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());try {return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());}catch (UnsupportedOperationException ex) {...}}// ...
}
  • 寻找@Value:判断属性字段是否标记为@Value
// QualifierAnnotationAutowireCandidateResolver#findValue
@Nullable
protected Object findValue(Annotation[] annotationsToSearch) {if (annotationsToSearch.length > 0) {   // qualifier annotations have to be localAnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(// valueAnnotationType即为@ValueAnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);if (attr != null) {return extractValue(attr);}}return null;
}
  • 解析@Value的字符串值:如果字段标记了@Value,则可拿到对应的字符串值,然后就可以根据字符串值去做解析,解析结果可能是字符串,也可能是对象

  • 将解析结果转化为要装配的对象的类型:

分析完对应的步骤后,可以定位到问题原因在解析@Value指定字符串过程中,对${xxx}的查找不局限在application.properties,而是针对多个源,这些源在启动时被有序固定,所以在查找时也是按序查找的。当查找到systemEnvironment时发有个username和配置文件中的重合
在这里插入图片描述

// PropertySourcesPropertyResolver#getProperty
@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {if (this.propertySources != null) {for (PropertySource<?> propertySource : this.propertySources) {if (logger.isTraceEnabled()) {logger.trace("Searching for key '" + key + "' in PropertySource '" +propertySource.getName() + "'");}Object value = propertySource.getProperty(key);if (value != null) {if (resolveNestedPlaceholders && value instanceof String) {value = resolveNestedPlaceholders((String) value);}logKeyFound(key, propertySource, value);return convertValueIfNecessary(value, targetValueType);}}}if (logger.isTraceEnabled()) {logger.trace("Could not find key '" + key + "' in any property source");}return null;
}

解决方式

避免存在与系统或环境变量有同名的配置

myname=admin
password=pass

错乱的注入集合

问题

假设存在多个学生Bean,需找出来并存储到List里并在控制器类中输出:

// 可以理解为收集方式
@Bean
public Student student1(){return createStudent(1, "psj1");
}@Bean
public Student student2(){return createStudent(2, "psj2");
}private Student createStudent(int id, String name) {Student student = new Student();student.setId(id);student.setName(name);return student;
}
private List<Student> students;public StudentController(List<Student> students){this.students = students;
}@RequestMapping(path = "students", method = RequestMethod.GET)
public String listStudents(){return students.toString();
};

此时需要再增加学生Bean,换了一种方式注入集合类型:

// 可以理解为直接装配方式
@Bean
public List<Student> students(){Student student3 = createStudent(3, "psj3");Student student4 = createStudent(4, "psj4");return Arrays.asList(student3, student4);
}

但上述两种方式都存在,只会输出前面两个学生

原因

  • 进行第一种装配方式时(即收集方式),主要分为以下过程:
// DefaultListableBeanFactory#resolveMultipleBeans
@Nullable
private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {final Class<?> type = descriptor.getDependencyType();if (descriptor instanceof StreamDependencyDescriptor) {...// 装配streamreturn stream;}else if (type.isArray()) {...// 装配数组return result;}else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {// 装配集合// 获取集合的元素类型Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();if (elementType == null) {return null;}// 根据元素类型查找所有的beanMap<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,new MultiElementDescriptor(descriptor));if (matchingBeans.isEmpty()) {return null;}if (autowiredBeanNames != null) {autowiredBeanNames.addAll(matchingBeans.keySet());}// 转化查到的所有bean放置到集合并返回TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());Object result = converter.convertIfNecessary(matchingBeans.values(), type);...return result;}else if (Map.class == type) {...// 解析mapreturn matchingBeans;}else {return null;}
}
  • 进行第二种装配方式时(即直接装配方式),具体过程在DefaultListableBeanFactory#findAutowireCandidates
  • 当同时满足这两种装配方式时,从下面代码中可以看出它们是不能共存的:
// DefaultListableBeanFactory#doResolveDependency
// 采用收集方式
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {return multipleBeans;
}
// 上述方式不执行才会执行直接装配方式
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);

解决方式

对于同一个集合对象的注入不要混合多种注入方式

参考

极客时间-Spring 编程常见错误 50 例

https://github.com/jiafu1115/springissue/tree/master/src/main/java/com/spring/puzzle/class3

相关文章:

Spring编程常见错误50例-Spring Bean依赖注入常见错误(下)

Value没有注入预期的值 问题 对于Value可以装配多种类型的数据&#xff1a; 装配对象&#xff1a; Value("#{student}") private Student student;Bean public Student student(){Student student createStudent(1, "xie");return student; }装配字符…...

SpringBoot整合Canal实现MySQL与ES数据同步

文章目录 SpringBoot项目引入Canal依赖配置文件项目结构设置监听类其余类、接口内容启动类实体类Controller类Mapper接口Serice接口 运行测试 开始之前请确认docker中已运行mysql与canal容器&#xff0c;并完成了监听binlog配置 未完成可移步&#xff1a; Docker部署Canal监听…...

Zookeeper 源码分析流程

文章目录 前言Zookeeper启动加载磁盘数据与客户端的通信交互Leader选举准备节点状态处理总结 前言 Zookeeper 作为分布式协调服务为分布式系统提供了一些基础服务&#xff0c;如&#xff1a;命名服务、配置管理、同步等&#xff0c;使得开发者可以更加轻松地处理分布式问题。 …...

计数排序与基数排序

计数排序与基数排序 计数排序 计数排序&#xff1a;使用一个数组记录序列中每一个数字出现的次数&#xff0c;将该数组的下标作为实际数据&#xff0c;元素的值作为数据出现的次数。例如对于序列[3,0,1,1,3,3,0,2]&#xff0c;统计的结果为&#xff1a; 0出现的次数&#xf…...

Mysql—表操作

目录 1、linux中数据库表名区分大小写&#xff0c;windows不区分2、创建数据库表3、外键4、查看数据表结构5、修改表5.1、修改表名5.2、添加字段5.3、指定位置添加字段5.4、修改字段名称5.5、修改字段类型5.6、修改字段位置5.7、删除字段5.8、修改表存储引擎5.9、删除外键 1、l…...

SpringCloud——微服务

微服务技术栈 在之前的开发过程中&#xff0c;我们将所有的服务都部署在一台服务器中&#xff0c;当我们的服务开始越来越多&#xff0c;业务越来越复杂&#xff0c;当一台服务器不能承担我们的业务的时候&#xff0c;就需要将不同的业务分开部署在不同的服务器上&#xff0c;…...

深入理解Java单例模式和优化多线程任务处理

目录 饿汉模式懒汉模式单线程版多线程版双重检查锁定 阻塞队列 单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例&#xff0c;并提供一个全局访问点。 饿汉模式 类加载的同时&#xff0c;创建实例。 class Singleton {private static final Singlet…...

已解决 Kotlin Error: Type mismatch: inferred type is String but Int was expected

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页: &#x1f405;&#x1f43e;猫头虎的博客&#x1f390;《面试题大全专栏》 &#x1f995; 文章图文并茂&#x1f996…...

Web应用系统的小安全漏洞及相应的攻击方式

写作目的 本文讲述一个简单的利用WebAPI来进行一次基本没有破坏力的“黑客”行为。 主要目的如下&#xff1a; 了解什么叫安全漏洞 知道什么是api 了解一些获取api的工具 通过对API的认识了解白盒接口测试基本概念和技术 免责声明&#xff1a; 本文主要是以学习交流为目的&a…...

git工具下载和安装

(1)从git官网下载安装包 然后安装 https://git-scm.com/downloads (2)git 学习参考官方的资料 https://git-scm.com/book/en/v2...

腾讯mini项目-【指标监控服务重构】2023-08-04

今日已办 关于 span-references 的调研 https://github.com/DataDog/dd-trace-js/issues/1761 https://github.com/open-telemetry/opentelemetry-specification/blob/874a451e7f6ac7fc54423ee3f03e5394197be35b/specification/compatibility/opentracing.md#span-references h…...

怎么推广自己抖店的商品?最适合0经验新手操作的办法,来看看

我是王路飞。 抖店开通后&#xff0c;想要把自己店铺的商品卖出去&#xff0c;就需要进行推广了。 但是怎么推广呢&#xff1f; 要么利用抖音的搜索和推荐流量&#xff0c;获取曝光&#xff0c;实现点击和转化。 不过这种玩法有个弊端&#xff0c;就是需要你有一定的电商经…...

线性代数的本质(三)——线性方程组

文章目录 线性方程组高斯消元法初等行变换线性方程组的解向量方程齐次线性方程组的解非齐次线性方程组的解 线性方程组 高斯消元法 客观世界最简单的数量关系是均匀变化的关系。在均匀变化问题中&#xff0c;列出的方程组是一次方程组&#xff0c;我们称之为线性方程组(Linea…...

轻量级性能测试工具 wrk 如何使用?

项目设计之初或者是项目快要结束的时候&#xff0c;大佬就会问我们&#xff0c;这个服务性能测试的结果是什么&#xff0c;QPS 可以达到多少&#xff0c;RPS 又能达到多少&#xff1f;接口性能可以满足未来生产环境的实际情况吗&#xff1f;有没有自己测试过自己接口的吞吐量&a…...

WebGL 视图矩阵、模型视图矩阵

目录 立方体由三角形构成 视点和视线 视点、观察目标点和上方向 视点&#xff1a; 观察目标点&#xff1a; 上方向&#xff1a; 在WebGL中&#xff0c;观察者的默认状态应该是这样的&#xff1a; 视图矩阵程序&#xff08;LookAtTriangles.js&#xff09; 实际上&…...

Python 3 – 文件 readline() 方法

Python 3 – 文件 readline() 方法|极客笔记 # 打开文件 file open("example.txt", "r")# 读取文件中的一行数据 line file.readline() while line:# 移除行尾的换行符print(line.strip())# 读取文件中的下一行数据line file.readline()# 关闭文件 file…...

如何在微软Edge浏览器上一键观看高清视频?

编者按&#xff1a;视频是当下最流行的媒体形式之一。但由于视频压缩、网络不稳定等原因&#xff0c;我们常常可以看到互联网上的很多视频其画面质量并不理想&#xff0c;尤其是在浏览器端&#xff0c;这极大地影响了观看体验。不过&#xff0c;近期微软 Edge 浏览器推出了一项…...

Telegram BoT的主流项目盘点

目录 DeFi 类 数据分析类 空投埋伏交易 其他 Telegram Bot赛道的发展趋势预测 Telegram BoT赛道发展较快&#xff0c;具体来看可以分为DeFi 类、数据分析类、空投埋伏交易类以及其他。 DeFi 类 Unibot&#xff08;交易&#xff09;、Banana Gun、WagieBot&#xff08;交…...

PTA 甲级 1044 Shopping in Mars

题目链接 思路&#xff1a;前缀和滑动窗口 #include<bits/stdc.h> #define MAXN 100010 using namespace std; int a[MAXN];int main(){int n,m;cin>>n>>m;//n数量 m金额for(int i1;i<n;i){int t;cin>>t;a[i]a[i-1]t;//前缀和}vector<pair<in…...

Linux学习之MyCat实现分库分表

环境准备 先准备一套MySQL主从服务器&#xff0c;可参考MySQL主从配置配置MyCat服务 资源下载 网盘链接: https://pan.baidu.com/s/1cLTMH_e1-6loc_gF9ZNHTg?pwda63n 提取码: a63n MyCat配置 # 1&#xff09;安装mycat软件 //安装jdk [rootmycat58 upload]# yum -y insta…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

日常一水C

多态 言简意赅&#xff1a;就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过&#xff0c;当子类和父类的函数名相同时&#xff0c;会隐藏父类的同名函数转而调用子类的同名函数&#xff0c;如果要调用父类的同名函数&#xff0c;那么就需要对父类进行引用&#…...

LOOI机器人的技术实现解析:从手势识别到边缘检测

LOOI机器人作为一款创新的AI硬件产品&#xff0c;通过将智能手机转变为具有情感交互能力的桌面机器人&#xff0c;展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家&#xff0c;我将全面解析LOOI的技术实现架构&#xff0c;特别是其手势识别、物体识别和环境…...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...

Python网页自动化Selenium中文文档

1. 安装 1.1. 安装 Selenium Python bindings 提供了一个简单的API&#xff0c;让你使用Selenium WebDriver来编写功能/校验测试。 通过Selenium Python的API&#xff0c;你可以非常直观的使用Selenium WebDriver的所有功能。 Selenium Python bindings 使用非常简洁方便的A…...