注解说明

@Value 是Spring提供用于处理属性与表达式注入的注解,可标注在字段、方法或构造器的参数上,并且支持动态解析SpringMVC中的方法参数。支持注入方式:

  1. 直接注入如: @Value(“test”)
  2. ${my.app.myProp} 属性注入
  3. #{systemProperties.myProp} SpEL表达式

它提供了不同的配置源(属性文件、系统属性)的注入

注解属性

@Value 注解只有一个属性 value ,表示要注入的实际值的表达式,支持属性与SpEL表达式两种方式

测试代码

以下测试代码MyController的username的注入值为: tomyli,而不是配置文件中的admin

 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
@Controller
public class MyController {

    @Value("${USER}")
    @Getter
    private String username;

}

@Configuration
@ComponentScan(basePackages = "cn.imcompany.bean.autowired")
@PropertySource("classpath:application.properties")
public class MyConfiguration {

}

public class Main {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfiguration.class);
        MyController myController = context.getBean(MyController.class);
        System.out.println(myController.getUsername()); // tomyi
    }
}

application.properties配置文件

1
USER=admin

源码阅读

@Value 注解的处理由 AutowiredAnnotationBeanPostProcessor 类负责,与处理 @Autowired 注解的类为同一个PostProcessor。主要的处理逻辑实现在 DefaultListableBeanFactory#doResolveDependency 方法,对于 @Value注解的处理主要集中在第2步,解析@Value的操作主要分为3步

整体调用图

解析配置的value属性值

首先会调用 QualifierAnnotationAutowireCandidateResolver#getSuggestedValue 来确认属性 value 的值,确认逻辑由 findValue() 方法执行,此方法会查询 @Value 注解并解析出配置的属性 value 值

查找解析值

解析到配置的属性value后,判断其为 String 类型,则调用 resolveEmbeddedValue() 方法对配置的表达式进行解析,在此方法中会循环遍历 embeddedValueResolvers 来调用 resolver的 resolveStringValue() 来解析值,这个String的解析操作会交给 AbstractEnvironment#resolvePlaceholders 方法,进而委托给 AbstractPropertyResolver#resolvePlaceholders 方法来解析具体的值配置信息,而获取配置的具体值的操作发生在 PropertySourcesPropertyResolver#getProperty , 在此方法中会循环遍历 propertySources,propertySources 中持有多个 propertySource,依次对每个 propertySource 调用 getProperty() 来查询值,找到一个满足条件的值后就返直接返回了,此时 propertySource 的顺序决定了查找资源的顺序

由图可知,查找的顺序为: systemProperties->systemEnvironment->application.properties 。调试发现在 systemEnvironment中查找到了 USER 的值 tomyli ,找到后就返回,不会再查 application.properties 文件中配置的 USER 的值了

AbstractEnvironment实现了PropertyResolver接口

解析嵌入的值,实际上就是替换占位符的工作

Resolver在什么时候设置的?

finishBeanFactoryInitialization 阶段,Spring会判断当前beanFactory是否存在值解析Resolver,没有则进行添加,而此时添加Resolver的解析就是 AbstractEnvironment类的resolvePlaceholders方法

StringValueResolver 被标识为 @FunctionalInterface

propertySources是在哪设置的?

systemProperties 和 systemEnvironment 是在 StandardEnvironment#customizePropertySources 添加的

  1. systemProperties系统变量主要包含Java系统相关信息(版本、VM信息)和系统相关信息(操作系统版本名称架构、文件分隔符等)和用户信息组成
  2. systemEnvironment系统环境变量主要为当前运行系统的信息,包含shell、用户登录名、PATH等变量信息

application.properties 文件配置是在 PropertySourceProcessor#processPropertySource 方法通过调用 addPropertySource() 时添加的

转换值为必要的类型

查找到值会调用 TypeConverterSupport#convertIfNecessary 来对获取的值进行类型转换,此方法会委托 TypeConverterDelegate类的convertIfNecessary 方法来进行真正的类型转换,在此方法会对常见的Java标准类型(数组、集合、Map、String、Number)与期望的类型进行比较,匹配上进行相应类型的转换

总结

@Value注解是Spring提供的用户注入属性的注解,其主要处理类为 AutowiredAnnotationPostProcessor ,解析操作分为3步:

  1. 解析属性value值
  2. 查找配置的属性值对应的真实值
  3. 找到后进行相应的类型转换

在配置要注入的值时需要关注 @Value 注解属性配置的查找顺序,Spring会先查询 systemProperties和systemEnvironment 中的配置,匹配到就返回,想要让配置在application.properties中的配置有效,注意不要用系统的参数名相同