热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Day604.@Value注入问题集合类型注入问题Spring编程常见错误

Value注入问题&集合类型注入问题讲到Spring的反转注入,必然知道他的强大,当这次今天阿昌总结的两种问题,当Value和Sprin

@Value注入问题&集合类型注入问题

讲到Spring的反转注入,必然知道他的强大,当这次今天阿昌总结的两种问题,当@Value和Spring注入集合类型有可能会发生的问题如下:


一、@Value 没有注入预期的值

当使用@Value,大部分的人可能觉得他只会用于在去注入String类型的场景,当其实他也可以去注入对象类型。

对于对象类型的注入,大部分人都会选择去使用@Autowired 或者 @Resource的方式去注入,而不会选择@Value。

举例@Autowired 和 @Value 的区别,前者可以在使用注解的时候不给予参数注明,而后者必须要求以表达式的方式去指定要注入的内容的位置。



@Value注解源码 内容如下:↓

public @interface Value {/*** The actual value expression — for example, #{systemProperties.myProp}.*/String value();
}

我们一般都会因为 @Value 常用于 String 类型的装配而误以为 @Value 不能用于非内置对象的装配,实际上这是一个常见的误区

@Value("#{student}")
private Student student;

如上的方式可以在ioc容器中寻找beanid为student的bean实例进行注入



当然,使用 @Value 更多是用来装配 String,而且它支持多种强大的装配方式,典型的方式参考下面的示例:

//注册正常字符串
@Value("我是字符串")
private String text; //注入系统参数、环境变量或者配置文件中的值
@Value("${ip}")
private String ip//注入其他Bean属性,其中student为bean的ID,name为其属性
@Value("#{student.name}")
private String name;

所以上面就会涉及到,他去哪个配置源去取对应的内容进行注入?



当我们在application.properties中配置如下配置,

username=admin
password=pass

再用@Value去取的时候会出现问题!

@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 时,我们会发现 password 正确返回了,但是 username 返回的并不是配置文件中指明的 admin,而是运行这段程序的计算机用户名。

很明显,使用 @Value 装配的值没有完全符合我们的预期。



那为什么呢?
在Spring对应@Value注解的处理的源码如下:


@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, &#64;Nullable String beanName,&#64;Nullable Set<String> autowiredBeanNames, &#64;Nullable TypeConverter typeConverter) throws BeansException {//省略其他非关键代码Class<?> type &#61; descriptor.getDependencyType();//寻找&#64;ValueObject value &#61; getAutowireCandidateResolver().getSuggestedValue(descriptor);if (value !&#61; null) {if (value instanceof String) {//解析Value值String strVal &#61; resolveEmbeddedValue((String) value);BeanDefinition bd &#61; (beanName !&#61; null && containsBean(beanName) ?getMergedBeanDefinition(beanName) : null);value &#61; evaluateBeanDefinitionString(strVal, bd);}//转化Value解析的结果到装配的类型TypeConverter converter &#61; (typeConverter !&#61; null ? typeConverter : getTypeConverter());try {return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());}catch (UnsupportedOperationException ex) {//异常处理}}//省略其他非关键代码}

主要设计三步&#xff1a;


  • 寻找 &#64;Value

    • 这里就涉及到他去哪个配置源取对应的配置
      在这里插入图片描述
  • 解析 &#64;Value 的字符串值

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

    • 拿到数据后&#xff0c;并赋值装配给我们指定的对象


那上面的问题我们就可以知道为什么username的取值会出现问题&#xff1a;刚好系统环境变量&#xff08;systemEnvironment&#xff09;中含有同名的配置

所以命名时&#xff0c;我们一定要注意不仅要避免和环境变量冲突&#xff0c;也要注意避免和系统变量等其他变量冲突&#xff0c;这样才能从根本上解决这个问题。




二、集合类型注入错乱问题


1、收集装配

对于Spring本身的特别强大的&#xff0c;他可以用于对于bean对象进行收集装配合集类型的注入&#xff0c;举例如下&#xff1a;↓

&#64;Bean
public Student student1(){return createStudent(1, "xie");
}&#64;Bean
public Student student2(){return createStudent(2, "fang");
}private Student createStudent(int id, String name) {Student student &#61; new Student();student.setId(id);student.setName(name);return student;
}

有了集合类型的自动注入后&#xff0c;我们就可以把零散的学生 Bean 收集起来了&#xff0c;代码示例如下&#xff1a;

&#64;RestController
&#64;Slf4j
public class StudentController {private List<Student> students;//他会直接收集ioc容器中的bean&#xff0c;并装配到这个集合中//我们这里使用的构造器注入装配&#xff0c;所以不需要使用&#64;Autowiredpublic StudentController(List<Student> students){this.students &#61; students;}&#64;RequestMapping(path &#61; "students", method &#61; RequestMethod.GET)public String listStudents(){return students.toString();};
}



2、直接装配

当然我们也可以直接使用如下的方式直接进行直接装配的方式&#xff1a;↓

&#64;Bean
public List<Student> students(){Student student3 &#61; createStudent(3, "liu");Student student4 &#61; createStudent(4, "fu");return Arrays.asList(student3, student4);
}



3、当两种方式同时存在的问题

当Spring对于集合类型装配的源码如下&#xff1a;


private Object resolveMultipleBeans(DependencyDescriptor descriptor, &#64;Nullable String beanName,&#64;Nullable Set<String> autowiredBeanNames, &#64;Nullable TypeConverter typeConverter) {final Class<?> type &#61; descriptor.getDependencyType();if (descriptor instanceof StreamDependencyDescriptor) {//装配streamreturn stream;}else if (type.isArray()) {//装配数组return result;}else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {//装配集合//获取集合的元素类型Class<?> elementType &#61; descriptor.getResolvableType().asCollection().resolveGeneric();if (elementType &#61;&#61; null) {return null;}//根据元素类型查找所有的beanMap<String, Object> matchingBeans &#61; findAutowireCandidates(beanName, elementType,new MultiElementDescriptor(descriptor));if (matchingBeans.isEmpty()) {return null;}if (autowiredBeanNames !&#61; null) {autowiredBeanNames.addAll(matchingBeans.keySet());}//转化查到的所有bean放置到集合并返回TypeConverter converter &#61; (typeConverter !&#61; null ? typeConverter : getTypeConverter());Object result &#61; converter.convertIfNecessary(matchingBeans.values(), type);//省略非关键代码return result;}else if (Map.class &#61;&#61; type) {//解析mapreturn matchingBeans;}else {return null;}
}

如要的步骤如下&#xff1a;


  • 获取集合类型的元素类型
    • 针对本案例&#xff0c;目标类型定义为 List students&#xff0c;所以元素类型为 Student&#xff0c;获取的具体方法参考代码行&#xff1a;
  • 根据元素类型&#xff0c;找出所有的 Bean
    • 有了上面的元素类型&#xff0c;即可根据元素类型来找出所有的 Bean&#xff0c;关键代码行如下&#xff1a;
  • 将匹配的所有的 Bean 按目标类型进行转化


Spring在装配对于的方式如上面两种方式的选择上的源码如下&#xff1a;

Object multipleBeans &#61; resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans !&#61; null) {return multipleBeans;
}
Map<String, Object> matchingBeans &#61; findAutowireCandidates(beanName, type, descriptor);

当resolveMultipleBeans转换解析成功后就直接return了。我们看到Spring的做法是这两种装配集合的方式是不能同存的。

结合本案例&#xff0c;当使用收集装配方式来装配时&#xff0c;能找到任何一个对应的 Bean&#xff0c;则返回&#xff0c;如果一个都没有找到&#xff0c;才会采用直接装配的方式。说到这里&#xff0c;你大概能理解为什么后期以 List 方式直接添加的 Student Bean 都不生效了吧。

那么可知解决方案&#xff0c;就是使用一个方式进行对集合类型方式的注入。

直接装配&#xff1a;↓

&#64;Bean
public List<Student> students(){Student student1 &#61; createStudent(1, "xie");Student student2 &#61; createStudent(2, "fang");Student student3 &#61; createStudent(3, "liu");Student student4 &#61; createStudent(4, "fu");return Arrays.asList(student1&#xff0c;student2&#xff0c;student3, student4);
}

收集方式&#xff1a;↓

&#64;Beanpublic Student student1(){return createStudent(1, "xie");}&#64;Beanpublic Student student2(){return createStudent(2, "fang");}&#64;Beanpublic Student student3(){return createStudent(3, "liu");}&#64;Beanpublic Student student4(){return createStudent(4, "fu");}

在对于同一个集合对象的注入上&#xff0c;混合多种注入方式是不可取的&#xff0c;这样除了错乱&#xff0c;别无所得。



那最后&#xff0c;在案例 2 中&#xff0c;我们初次运行程序获取的结果如下&#xff1a;[Student(id&#61;1, name&#61;xie), Student(id&#61;2, name&#61;fang)]那么如何做到让学生 2 优先输出呢&#xff1f;


  1. 在代码层&#xff0c;直接去改变他们两个new出来的顺序
  2. 添加&#64;Order(number)注解&#xff0c;number越小优先级越高&#xff0c;越靠前
  3. &#64;DependsOn 使用它&#xff0c;可使得依赖的Bean如果未被初始化会被优先初始化。


推荐阅读
  • 原文网址:https:www.cnblogs.comysoceanp7476379.html目录1、AOP什么?2、需求3、解决办法1:使用静态代理4 ... [详细]
  • 如何在方法上应用@ConfigurationProperties注解进行属性绑定 ... [详细]
  • 秒建一个后台管理系统?用这5个开源免费的Java项目就够了
    秒建一个后台管理系统?用这5个开源免费的Java项目就够了 ... [详细]
  • 深入解析Struts、Spring与Hibernate三大框架的面试要点与技巧 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 在Java Web服务开发中,Apache CXF 和 Axis2 是两个广泛使用的框架。CXF 由于其与 Spring 框架的无缝集成能力,以及更简便的部署方式,成为了许多开发者的首选。本文将详细介绍如何使用 CXF 框架进行 Web 服务的开发,包括环境搭建、服务发布和客户端调用等关键步骤,为开发者提供一个全面的实践指南。 ... [详细]
  • 《Spring in Action 第4版:全面解析与实战指南》
    《Spring in Action 第4版:全面解析与实战指南》不仅详细介绍了Spring框架的核心优势,如简洁易测试、低耦合特性,还深入探讨了其轻量级和最小侵入性的设计原则。书中强调了声明式编程的优势,并通过基于约定的方法简化开发流程。此外,Spring的模板机制有效减少了重复代码,而依赖注入功能则由容器自动管理,确保了应用的灵活性和可维护性。 ... [详细]
  • Spring Boot 中配置全局文件上传路径并实现文件上传功能
    本文介绍如何在 Spring Boot 项目中配置全局文件上传路径,并通过读取配置项实现文件上传功能。通过这种方式,可以更好地管理和维护文件路径。 ... [详细]
  • 本文对比了杜甫《喜晴》的两种英文翻译版本:a. Pleased with Sunny Weather 和 b. Rejoicing in Clearing Weather。a 版由 alexcwlin 翻译并经 Adam Lam 编辑,b 版则由哈佛大学的宇文所安教授 (Prof. Stephen Owen) 翻译。 ... [详细]
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • 本文详细解析了使用C++实现的键盘输入记录程序的源代码,该程序在Windows应用程序开发中具有很高的实用价值。键盘记录功能不仅在远程控制软件中广泛应用,还为开发者提供了强大的调试和监控工具。通过具体实例,本文深入探讨了C++键盘记录程序的设计与实现,适合需要相关技术的开发者参考。 ... [详细]
  • 深入理解Java中的多态性概念及其应用
    多态是面向对象编程中的三大核心特性之一,与封装和继承共同构成了面向对象的基础。多态使得代码更加灵活和可扩展,封装和继承则为其提供了必要的支持。本文将深入探讨多态的概念及其在Java中的具体应用,帮助读者全面理解和掌握这一关键知识点。 ... [详细]
  • 深入理解排序算法:集合 1(编程语言中的高效排序工具) ... [详细]
  • Squaretest:自动生成功能测试代码的高效插件
    本文将介绍一款名为Squaretest的高效插件,该工具能够自动生成功能测试代码。使用这款插件的主要原因是公司近期加强了代码质量的管控,对各项目进行了严格的单元测试评估。Squaretest不仅提高了测试代码的生成效率,还显著提升了代码的质量和可靠性。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
author-avatar
天河体育场里的执波仔_287
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有