我是靠谱客的博主 笨笨眼睛,这篇文章主要介绍Spring编程常见错误--Spring Core篇-03 |Spring Bean 依赖注入常见错误(下)三、Spring Bean 依赖注入常见错误(下),现在分享给大家,希望可以做个参考。
三、Spring Bean 依赖注入常见错误(下)
Spring 的自动注入
一.@Value 没有注入预期的值:@Value查找值的范围不局限于application.property 文件
1、代码
1.application.properties
复制代码
1
2
3username=admin password=pass
2.Controller
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13@RestController 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; } }
2、案例解析
password 正确返回了,但是 username 返回的并不是配置文件中指明的 admin,而是运行这段程序的计算机用户名。很明显,使用 @Value 装配的值没有完全符合我们的预期。
3、@Value 的工作的三个核心步骤
1.寻找 @Value
在这步中,主要是判断这个属性字段是否标记为 @Value,依据的方法参考 QualifierAnnotationAutowireCandidateResolver.findValue
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13@Nullable protected Object findValue(Annotation[] annotationsToSearch) { if (annotationsToSearch.length > 0) { AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); //valueAnnotationType即为@Value if (attr != null) { return extractValue(attr); } } return null; }
2.解析 @Value 的字符串值
如果一个字段标记了 @Value,则可以拿到对应的字符串值,然后就可以根据字符串值去做解析,最终解析的结果可能是一个字符串,也可能是一个对象,这取决于字符串怎么写。
3.将解析结果转化为要装配的对象的类型
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14@Nullable protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) { if (this.propertySources != null) { for (PropertySource<?> propertySource : this.propertySources) { Object value = propertySource.getProperty(key); if (value != null) { //查到value即退出 return convertValueIfNecessary(value, targetValueType); } } } return null; }
4、@Value读取值的文件
复制代码
1
2
3
4
5
6[ConfigurationPropertySourcesPropertySource {name='configurationProperties'}, StubPropertySource {name='servletConfigInitParams'}, ServletContextPropertySource {name='servletContextInitParams'}, PropertiesPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: classpath:/application.properties]'}, MapPropertySource {name='devtools'}]
5、问题修正
不仅要避免和环境变量冲突,也要注意避免和系统变量等其他变量冲突,这样才能从根本上解决这个问题。
复制代码
1
2
3spring.application.username=admin spring.application.password=pass
二.错乱的注入集合:零散注入和集中注入不能共存。
1、代码
1.Controller
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13@RestController public class StudentController { private List<Student> students; public StudentController(List<Student> students) { this.students = students; } @RequestMapping(path = "students", method = RequestMethod.GET) public String listStudents() { return students.toString(); } }
2.domain
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29public class Student { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + ''' + '}'; } }
3.零散注入
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15@Bean public Student student1(){ return createStudent(1, "xie"); } @Bean public Student student2(){ return createStudent(2, "fang"); } private Student createStudent(int id, String name) { Student student = new Student(); student.setId(id); student.setName(name); return student; }
4.集中注入
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13private Student createStudent(int id, String name) { Student student = new Student(); student.setId(id); student.setName(name); return student; } @Bean public List<Student> students(){ Student student3 = createStudent(3, "liu"); Student student4 = createStudent(4, "fu"); return Arrays.asList(student3, student4); }
2、案例解析
零散注入和集中注入,两种方式分别执行的时候,都可以生效,但是当一起的时候,集中注入无法生效。
Spring 使用的是 DefaultListableBeanFactory.resolveMultipleBeans 来完成装配工作
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) { final Class<?> type = descriptor.getDependencyType(); if (descriptor instanceof StreamDependencyDescriptor) { //装配stream return 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; } //根据元素类型查找所有的bean Map<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) { //解析map return matchingBeans; } else { return null; } }
1.获取集合类型的元素类型
复制代码
1
2
3//获取集合的元素类型 Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
2.根据元素类型,找出所有的 Bean
复制代码
1
2
3
4//根据元素类型查找所有的bean Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, new MultiElementDescriptor(descriptor));
3.将匹配的所有的 Bean 按目标类型进行转化
复制代码
1
2Object result = converter.convertIfNecessary(matchingBeans.values(), type);
3、问题修正
当使用收集装配方式来装配时,能找到任何一个对应的 Bean,则返回,如果一个都没有找到,才会采用直接装配的方式。
避免两种方式同时使用即可。
最后
以上就是笨笨眼睛最近收集整理的关于Spring编程常见错误--Spring Core篇-03 |Spring Bean 依赖注入常见错误(下)三、Spring Bean 依赖注入常见错误(下)的全部内容,更多相关Spring编程常见错误--Spring内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复