@Value 通常用于注入外部化属性

  1. @Component
  2. public class MovieRecommender {
  3. private final String catalog;
  4. public MovieRecommender(@Value("${catalog.name}") String catalog) {
  5. this.catalog = catalog;
  6. }
  7. }

使用以下配置:

  1. @Configuration
  2. @PropertySource("classpath:application.properties")
  3. public class AppConfig { }

以及下面的 application.properties 文件:

  1. catalog.name=MovieCatalog

在这种情况下,catalog 参数和字段将等同于 MovieCatalog 值。

Spring 提供了一个默认的宽松的嵌入式值解析器。它将尝试解析属性值,如果无法解析,属性名称(例如${catalog.name})将被注入作为值。也就是说默认情况下如果你的配置文件中没有提供 catalog.name 这个配置项,那么 MovieRecommender.catalog 的值将会以 ${catalog.name}字符串赋值

如果你想对不存在的值保持严格的控制,你应该声明一个PropertySourcesPlaceholderConfigurer Bean,如下面的例子所示。

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
  5. return new PropertySourcesPlaceholderConfigurer();
  6. }
  7. }

:::tips 当使用 JavaConfig 配置 PropertySourcesPlaceholderConfigurer 时,@Bean 方法必须是静态的。 ::: 配置之后,上面那个例子如果没有配置项将会抛出一个异常,比如下面这个异常信息

  1. Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'catalog.name' in value "${catalog.name}"

使用上述配置可以确保在任何 ${}占位符无法解决的情况下 Spring 初始化失败。也可以使用setPlaceholderPrefix、setPlaceholderSuffix 或 setValueSeparator 等方法来定制占位符。

  1. @Bean
  2. public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
  3. final PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
  4. // 设置默认的占位符 ${} 为 $[]
  5. configurer.setPlaceholderPrefix("[");
  6. configurer.setPlaceholderSuffix("]");
  7. configurer.setValueSeparator(":"); // 变量默认值分隔符,比如 ${catalog.name:我是默认值},如果没有配置 catalog.name 则使用 : 后面的值注入
  8. return configurer;
  9. }

:::tips Spring Boot 默认配置了一个 PropertySourcesPlaceholderConfigurer Bean,它将从application.properties 和 application.yml 文件中获取属性。 :::

Spring 提供的内置转换器支持允许自动处理简单的类型转换(例如转换为 Integer 或 int)。多个逗号分隔的值可以自动转换为字符串数组,无需额外的努力。

可以提供如下的默认值:

  1. @Component
  2. public class MovieRecommender {
  3. private final String catalog;
  4. public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) {
  5. this.catalog = catalog;
  6. }
  7. }

Spring BeanPostProcessor 在幕后使用一个 ConversionService 来处理将 @Value 中的字符串值转换为目标类型的过程。如果你想为你自己的自定义类型提供转换支持,你可以提供你自己的 ConversionService Bean 实例,如下例所示。

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public ConversionService conversionService() {
  5. DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
  6. conversionService.addConverter(new MyCustomConverter());
  7. return conversionService;
  8. }
  9. }

@Value 包含一个 SpEL 表达式时,该值将在运行时被动态计算出来,如下例所示:

  1. @Component
  2. public class MovieRecommender {
  3. private final String catalog;
  4. public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
  5. this.catalog = catalog;
  6. }
  7. }

SpEL 还支持使用更复杂的数据结构:

  1. @Component
  2. public class MovieRecommender {
  3. private final Map<String, Integer> countOfMoviesPerCatalog;
  4. public MovieRecommender(
  5. @Value("#{{'Thriller': 100, 'Comedy': 300}}") Map<String, Integer> countOfMoviesPerCatalog) {
  6. this.countOfMoviesPerCatalog = countOfMoviesPerCatalog;
  7. }
  8. }

总结

出现了两种符号: $#,由于这里的总结没有相关文献,暂时从表现上来看:

  • $:对应的是配置文件中的值
  • #:对应的 spel 表达式,还可以引用一个 bean 中的属性,比如上面的 systemProperties