当前位置:   article > 正文

@Value的用法

@value

@Value属于spring的注解,在spring-beans包下,可以在 字段 或 方法参数 或 构造函数参数 上使用,通常用于属性注入。支持SpEL (Spring Expression Language)表达式来注入值,同时也支持属性占位符注入值。

篇幅较长,相对来说写的非常细了,基本上涉及到的面全有了,建议收藏慢慢看!

一、属性注入三种方式

使用@Value前提:

  1. 不能直接作用于静态变量(static);
  2. 不能直接作用于常量(final);
  3. 不能在非注册的类中使用(类需要被注册在spring上下文中,如用@Service,@RestController,@Component等);
  4. 使用这个类时,只能通过依赖注入的方式,用new的方式是不会自动注入这些配置的。

1.1.配置文件读取值

@Value(“${xxxx}”)注解从配置文件读取值的用法,也就是从application.yml / application.properties文件中获取值。

@Configuration
public class MyConfig {

    @Value("${spring.application.name}")
    private String name;

    public String getName() {
        return name;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

application.yml

spring:
  application:
    name: test
  • 1
  • 2
  • 3

成功获取到application当中的值。

在这里插入图片描述

倘若application当中没有配置spring.application.name这时候启动项目就报异常了。

在这里插入图片描述

我们可以这样写:@Value("${spring.application.name:test111}") 代表的是假如application当中读不到值,那么就使用test111。这样可以完全避免没有设置值而启动报错的问题。当然也可以不设置默认值,比如@Value("${spring.application.name:}") 这样同样可以避免没有设置值启动报错的问题!

1.2.直接赋值

@Value("张三")属性注入有点类似于直接给属性赋值一样,但是实际开发当中这种应用场景非常少。

@Configuration
public class MyConfig {

    @Value("张三")
    private String name;

    public String getName() {
        return name;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

1.3.SpEL 表达式注入

@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());
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

systemProperties是spring预定义的,我们可以拿来直接用的。

@Value("#{systemProperties['user.language']}")
  • 1

二、property标签

@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>
  • 1
  • 2
  • 3
  • 4

三、构造函数注入

如果需要读取配置文件application.yml 的属性值,只需要在变量上加 @Value("${属性名}") 注解,就可以将配置文件 application.yml 的一个属性值赋值给变量。但是,如果我们在对象的构造方法中使用这个变量,结果发现这个变量的值为null。

@Configuration
public class MyConfig {

    @Value("${spring.application.name}")
    private String name;

    public MyConfig() {
        System.out.println(name);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

原因很简单构造器优先于属性注入,以至于属性还没注入进去,构造器当然拿不到值了。 那么在构造方法中如果要使用配置文件中的属性值,该怎么使用呢?见下方代码:

@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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

四、方法注入

方法参数注入

@Configuration
public class MyConfig {

    @Bean
    public Student getName(@Value("${spring.application.name}")String name) {
        Student student = new Student();
        student.setName(name);
        return student;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

方法上注入,方法注入就是在方法上使用@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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

如果属性上使用了@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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

如果就想使用静态变量怎么办,其实是有很多种方案可以实现的。

5.1.通过方法注入

@Configuration
public class MyConfig {

    public static String name;

    @Value("${spring.application.name}")
    public void initName(String s) {
        name = s;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

5.2.通过@PostConstruct

Java中该注解的说明:@PostConstruct该注解被用来修饰一个非静态的void()方法。被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。

Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
  • 1
@Configuration
public class MyConfig {

    public static String name;

    @Value("${spring.application.name}")
    private String s;

    @PostConstruct
    public void init(){
        name = s;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

5.3.继承InitializingBean接口

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

静态变量一般不建议设置为public,我们可以通过static 的 get方法来获取,因为静态变量设置为public,一旦有些地方没注意到把变量给改变值了,那这个值就彻底改变了,除非重启项目。

六、给不同类型变量注入

6.1.集合和数组

test:
  array1: aaa,bbb,ccc
  array2: 111,222,333
  • 1
  • 2
  • 3

使用@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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

6.2.map

test:
  map1: '{"name": "zhangsan", "sex": "male"}' 
  map2: '{"math": "90", "english": "85"}'
  • 1
  • 2
  • 3
@Value("#{${test.map2}}")
private Map<String,String> map1;
@Value("#{${test.map2}}")
private Map<String,Integer> map2;
  • 1
  • 2
  • 3
  • 4

6.3.文件或url资源

@Value("classpath:com/gzl/spring/configinject/config.txt")
private Resource resourceFile; // 注入文件资源

@Value("http://www.baidu.com")
private Resource testUrl; // 注入URL资源
  • 1
  • 2
  • 3
  • 4
  • 5

七、大小写以及命名问题

不要在application.yml/properties文件中使用驼峰命名。尽量用-分割。
我看了一下原生框架的配置,发现人家确实没大小写。

file:
  winUploadPath: D:/opt/tongue/uploadPath
  • 1
  • 2

使用@Value("${file.win-upload-path}")照样可以读出winUploadPath的值。

@Configuration
public class MyConfig {

    @Value("${file.win-upload-path}")
    private String filePath;

    public String getFilePath() {
        return filePath;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

如果yml当中使用的win-upload-path,那么@value必须使用win-upload-path读,否则启动报错。

八、配合@PropertySource使用

springboot默认读取的都是application.yml,或者application.properties,但是有时候我们想把一些配置给独立起来,这时候可以采用@PropertySource。

新建properties,yml文件也是可以的,

在这里插入图片描述

demo.name=huang
demo.sex=1
demo.type=demo
  • 1
  • 2
  • 3
@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 + '\'' +
                '}';
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

@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 + '\'' +
                '}';
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

九、@Value源码

代码当中并没有set方法,那他是如何赋值给属性值的呢?

@Configuration
public class MyConfig {
    @Value("${spring.application.name}")
    public String name;
}
  • 1
  • 2
  • 3
  • 4
  • 5

@Value实际上是通过org.springframework.beans.factory.config.BeanPostProcessor来执行的。当然BeanPostProcessor是个接口,AutowiredAnnotationBeanPostProcessor是BeanPostProcessor的一个实现类,然后AutowiredAnnotationBeanPostProcessor负责检查是否有这个注解的存在。

在这里插入图片描述

AutowireCandidateResolver 的getSuggestedValue方法负责获取注解的value值。AutowireCandidateResolver实际上是个接口,真正的getSuggestedValue方法是访问的QualifierAnnotationAutowireCandidateResolver类当中的。

在这里插入图片描述

DefaultListableBeanFactory当中的resolveEmbeddedValue方法,通过表达式得到真正的值。

在这里插入图片描述

最终得到值通过反射Field的set赋值

在这里插入图片描述

官网源码:https://github.com/spring-projects/spring-framework/blob/main/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Value.java

写作不易,如果这篇文章有帮助到您,麻烦您给小编留个赞哈!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/496011
推荐阅读
相关标签
  

闽ICP备14008679号