概述
三、Spring Bean 依赖注入常见错误(下)
Spring 的自动注入
一.@Value 没有注入预期的值:@Value查找值的范围不局限于application.property 文件
1、代码
1.application.properties
username=admin
password=pass
2.Controller
@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
@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.将解析结果转化为要装配的对象的类型
@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读取值的文件
[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、问题修正
不仅要避免和环境变量冲突,也要注意避免和系统变量等其他变量冲突,这样才能从根本上解决这个问题。
spring.application.username=admin
spring.application.password=pass
二.错乱的注入集合:零散注入和集中注入不能共存。
1、代码
1.Controller
@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
public 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.零散注入
@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.集中注入
private 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 来完成装配工作
private 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.获取集合类型的元素类型
//获取集合的元素类型
Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
2.根据元素类型,找出所有的 Bean
//根据元素类型查找所有的bean
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,
new MultiElementDescriptor(descriptor));
3.将匹配的所有的 Bean 按目标类型进行转化
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
3、问题修正
当使用收集装配方式来装配时,能找到任何一个对应的 Bean,则返回,如果一个都没有找到,才会采用直接装配的方式。
避免两种方式同时使用即可。
最后
以上就是笨笨眼睛为你收集整理的Spring编程常见错误--Spring Core篇-03 |Spring Bean 依赖注入常见错误(下)三、Spring Bean 依赖注入常见错误(下)的全部内容,希望文章能够帮你解决Spring编程常见错误--Spring Core篇-03 |Spring Bean 依赖注入常见错误(下)三、Spring Bean 依赖注入常见错误(下)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复