案例1 :@Value没有注入预期的值
在日常开发中,我们经常使用@Value注解给属性注入字符串,它其实还可以注入对象,只是写法不同。示例如下。
@Value("#{student}")
private Student student;
@Value多种注入方式。示例如下。
//注册正常字符串
@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
然后注入到属性当中去,并通过接口输出出来:
@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;
};
}
getUser接口返回的username并不是我们预期的admin,而是运行这段程序的电脑用户名。
这是因为在spring中,@Value里面的替换占位符去替换获取值时,并不局限于application.properties配置文件,它还会去系统环境变量里去查找替换。如下图。
image.png
image.png
误打误撞,变量名是一样的,并且系统变量源的index在CopyOnWriteArrayList集合的前面,所以会先去系统变量源里找,自然就取到了电脑用户名。解决的办法更改下替换占位符就好了。
@Value("${user.name}")
private String username;
案例2 :错乱的注入集合
@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;
}
@Bean
public List<Student> students(){
Student student3 = createStudent(3, "liu");
Student student4 = createStudent(4, "fu");
return Arrays.asList(student3, student4);
}
@RestController
@Slf4j
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();
};
}
这段程序运行的结果是输出id为1和为2的学生,id为3和为4的学生会被直接忽略掉。第一种方式我们先称它为收集方式,第二种方式称为直接装配方式,当这两种方式只存在一种时,都会生效的。两种都存在时,很明显,收集方式是生效的,而直接装配方式是无效的。这是因为底层代码是,如果能找到对应类型的bean组件,则返回,如果一个都没有找到,才会采用直接装配的方式。底层代码如下。
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
结论就是,这两种方式不能共存,那么解决方法就是编写代码时之采取一种方式装配就行了。
@Bean
public List<Student> students(){
Student student1 = createStudent(1, "xie");
Student student2 = createStudent(2, "fang");
Student student3 = createStudent(3, "liu");
Student student4 = createStudent(4, "fu");
return Arrays.asList(student1,student2,student3, student4);
}
或者
@Bean
public Student student1(){
return createStudent(1, "xie");
}
@Bean
public Student student2(){
return createStudent(2, "fang");
}
@Bean
public Student student3(){
return createStudent(3, "liu");
}
@Bean
public Student student4(){
return createStudent(4, "fu");
}