@Value
属于spring
的注解,在spring-beans
包下,可以在 字段 或 方法参数 或 构造函数参数 上使用,通常用于属性注入。支持SpEL
(Spring Expression Language)表达式来注入值,同时也支持属性占位符注入值。
篇幅较长,相对来说写的非常细了,基本上涉及到的面全有了,建议收藏慢慢看!
使用@Value前提:
@Value(“${xxxx}”)
注解从配置文件读取值的用法,也就是从application.yml / application.properties文件中获取值。
@Configuration
public class MyConfig {
@Value("${spring.application.name}")
private String name;
public String getName() {
return name;
}
}
application.yml
spring:
application:
name: test
成功获取到application当中的值。
倘若application当中没有配置spring.application.name这时候启动项目就报异常了。
我们可以这样写:
@Value("${spring.application.name:test111}")
代表的是假如application当中读不到值,那么就使用test111。这样可以完全避免没有设置值而启动报错的问题。当然也可以不设置默认值,比如@Value("${spring.application.name:}")
这样同样可以避免没有设置值启动报错的问题!
@Value("张三")
属性注入有点类似于直接给属性赋值一样,但是实际开发当中这种应用场景非常少。
@Configuration
public class MyConfig {
@Value("张三")
private String name;
public String getName() {
return name;
}
}
@Value(“#{xxxx}”)
是SpEL表达式的形式。想要深入了解SpEL表达式的可以看这一篇文章:https://blog.csdn.net/weixin_43888891/article/details/127520555
JAVA获得系统配置文件的System Properties,其中里面有一个属性就是user.language===zh,其实在spring当中我们可以通过SpEL 表达式来获取System Properties当中的属性值。
public class SystemProperties {
public static void main(String[] args) {
Properties properties = System.getProperties();
Iterator<Map.Entry<Object, Object>> iterator = properties.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object> entry = iterator.next();
System.out.println(entry.getKey() + "===" + entry.getValue());
}
}
}
systemProperties是spring预定义的,我们可以拿来直接用的。
@Value("#{systemProperties['user.language']}")
@Value根本不是通过属性的
set
方法进行注入的,这个我也是亲自做了试验。@Value注解并不等于xml当中property标签,property标签是利用set方法来赋值的。并且property 当中的value属性也可以使用SpEL表达式。
<bean id="dog1" class="com.gzl.cn.springbootcache.config.Student">
<property name="name" value="#{systemProperties['user.language']}"/>
<property name="age" value="27"/>
bean>
如果需要读取配置文件application.yml
的属性值,只需要在变量上加 @Value("${属性名}")
注解,就可以将配置文件 application.yml
的一个属性值赋值给变量。但是,如果我们在对象的构造方法中使用这个变量,结果发现这个变量的值为null。
@Configuration
public class MyConfig {
@Value("${spring.application.name}")
private String name;
public MyConfig() {
System.out.println(name);
}
}
原因很简单构造器优先于属性注入,以至于属性还没注入进去,构造器当然拿不到值了。 那么在构造方法中如果要使用配置文件中的属性值,该怎么使用呢?见下方代码:
@Configuration
public class MyConfig {
@Value("${spring.application.name}")
private String name;
public String getName() {
return name;
}
public MyConfig(@Value("${spring.application.name}") String name1) {
System.out.println(name1);
}
}
方法参数注入
@Configuration
public class MyConfig {
@Bean
public Student getName(@Value("${spring.application.name}")String name) {
Student student = new Student();
student.setName(name);
return student;
}
}
方法上注入,方法注入就是在方法上使用@Value,然后在注入到容器的过程当中他会读取@Value的值,然后将值传参访问这个方法。
@Configuration
public class MyConfig {
private String name;
@Value("${spring.application.name}")
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
如果属性上使用了@Value然后set方法上也使用了@Value,那么这个属性最终会被赋值两次,保留下来的是set的值,因为set优先级比属性赋值要低。
使用@Value注解是不允许在static变量注入的,包括get方法也是,直接会获取null值。原因很简单,@Value围绕的是注入到spring容器当中的这个单例对象,而static是类变量,所以肯定不可以的。可以理解为 类变量初始化优先于spring对象注入,所以他无法注入进去。
@Configuration
public class MyConfig {
@Value("${spring.application.name}")
private static String name;
public static String getName() {
return name;
}
}
如果就想使用静态变量怎么办,其实是有很多种方案可以实现的。
@Configuration
public class MyConfig {
public static String name;
@Value("${spring.application.name}")
public void initName(String s) {
name = s;
}
}
Java中该注解的说明:@PostConstruct
该注解被用来修饰一个非静态的void()方法
。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。
Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
@Configuration
public class MyConfig {
public static String name;
@Value("${spring.application.name}")
private String s;
@PostConstruct
public void init(){
name = s;
}
}
InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法。
@Configuration
public class MyConfig implements InitializingBean {
public static String name;
@Value("${spring.application.name}")
private String s;
@Override
public void afterPropertiesSet() throws Exception {
name = s;
}
}
静态变量一般不建议设置为public,我们可以通过static 的 get方法来获取,因为静态变量设置为public,一旦有些地方没注意到把变量给改变值了,那这个值就彻底改变了,除非重启项目。
test:
array1: aaa,bbb,ccc
array2: 111,222,333
使用@Value("${ }")
是直接属性赋值,@Value("#{}")
是使用的SpEL表达式。SpEL表达式读是先读出来字符串,然后我们可以再进行处理。使用SpEL读不到的话是null,而使用属性赋值的话是空数组。
@Configuration
public class MyConfig {
// 数组
@Value("${test.array1:}")
private String[] array1;
// 集合
@Value("${test.array1:}")
private List<String> list1;
// 集合
@Value("#{'${test.array2}'.split(',')}")
private List<String> list2;
// 集合进一步做空数据处理,读不到值是一个null
@Value("#{'${test.list:}'.empty ? null : '${test.list:}'.split(',')}")
private List<String> testList;
}
test:
map1: '{"name": "zhangsan", "sex": "male"}'
map2: '{"math": "90", "english": "85"}'
@Value("#{${test.map2}}")
private Map<String,String> map1;
@Value("#{${test.map2}}")
private Map<String,Integer> map2;
@Value("classpath:com/gzl/spring/configinject/config.txt")
private Resource resourceFile; // 注入文件资源
@Value("http://www.baidu.com")
private Resource testUrl; // 注入URL资源
不要在application.yml/properties文件中使用驼峰命名。尽量用-分割。
我看了一下原生框架的配置,发现人家确实没大小写。
file:
winUploadPath: D:/opt/tongue/uploadPath
使用@Value("${file.win-upload-path}")
照样可以读出winUploadPath
的值。
@Configuration
public class MyConfig {
@Value("${file.win-upload-path}")
private String filePath;
public String getFilePath() {
return filePath;
}
}
如果yml当中使用的win-upload-path,那么@value必须使用win-upload-path读,否则启动报错。
springboot默认读取的都是application.yml,或者application.properties,但是有时候我们想把一些配置给独立起来,这时候可以采用@PropertySource。
新建properties,yml文件也是可以的,
demo.name=huang
demo.sex=1
demo.type=demo
@Component
@PropertySource(value = "demo.properties")
public class ReadByPropertySourceAndValue {
@Value("${demo.name}")
private String name;
@Value("${demo.sex}")
private int sex;
@Value("${demo.type}")
private String type;
@Override
public String toString() {
return "ReadByPropertySourceAndValue{" +
"name='" + name + '\'' +
", sex=" + sex +
", type='" + type + '\'' +
'}';
}
}
@PropertySource 和 @ConfigurationProperties配合使用
@Component
@PropertySource(value = "demo.properties")
@ConfigurationProperties(prefix = "demo")
public class ReadByPropertySourceAndValue {
private String name;
private int sex;
private String type;
@Override
public String toString() {
return "ReadByPropertySourceAndValue{" +
"name='" + name + '\'' +
", sex=" + sex +
", type='" + type + '\'' +
'}';
}
}
代码当中并没有set方法,那他是如何赋值给属性值的呢?
@Configuration
public class MyConfig {
@Value("${spring.application.name}")
public String name;
}
@Value实际上是通过org.springframework.beans.factory.config.BeanPostProcessor
来执行的。当然BeanPostProcessor是个接口,AutowiredAnnotationBeanPostProcessor
是BeanPostProcessor的一个实现类,然后AutowiredAnnotationBeanPostProcessor
负责检查是否有这个注解的存在。
而AutowireCandidateResolver 的getSuggestedValue
方法负责获取注解的value值。AutowireCandidateResolver实际上是个接口,真正的getSuggestedValue方法是访问的QualifierAnnotationAutowireCandidateResolver类当中的。
DefaultListableBeanFactory当中的resolveEmbeddedValue方法,通过表达式得到真正的值。
最终得到值通过反射Field的set赋值
写作不易,如果这篇文章有帮助到您,麻烦您给小编留个赞哈!