SpringBoot yml配置map,@Value属性注入问题

问题描述

最近在遇到一个问题,SpringBoot的yml中配置map,使用@Value注入会报错,
最开始的配置

  mapping:
    AAAA: AAAA
    BBBB: BBBB

代码如下:

 @Value("${mapping}")
  private Map<String, String> map;
  .....

启动就会报错:

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'TestController': Unsatisfied dependency expressed through field 'TestService'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'TestService': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'mapping' in value "${mapping}"
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:662) ~[spring-beans-5.3.29.jar:5.3.29]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:642) ~[spring-beans-5.3.29.jar:5.3.29]
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.29.jar:5.3.29]
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.29.jar:5.3.29]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) ~[spring-beans-5.3.29.jar:5.3.29]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) ~[spring-beans-5.3.29.jar:5.3.29]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.29.jar:5.3.29]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.29.jar:5.3.29]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.29.jar:5.3.29]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.29.jar:5.3.29]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.29.jar:5.3.29]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.29.jar:5.3.29]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:921) ~[spring-context-5.3.29.jar:5.3.29]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.29.jar:5.3.29]
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.15.jar:2.7.15]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) ~[spring-boot-2.7.15.jar:2.7.15]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.15.jar:2.7.15]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) ~[spring-boot-2.7.15.jar:2.7.15]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) ~[spring-boot-2.7.15.jar:2.7.15]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) ~[spring-boot-2.7.15.jar:2.7.15]

解决方案

解决方式有两种:
1.还是@Value
yml

mapping: "{
  'AAAA': 'AAAA',
  'BBBB': 'BBBB'
}"

其中"和’和,都是不可省略的,少了’会当作EL表达式变量。
代码:

 @Value("#{${mapping:{}}}")
  private Map<String, String> map;
  .....

其实就是使用el表达式解析。然后程序就可以正常使用了。
2. 使用properties配置就会自动解析

  • 代码
app:
  mapping:
    AAAA: AAAA
    BBBB: BBBB
  • 配置config
@Data
@ConfigurationProperties(prefix = "app")
public class TestConfig {

    private Map<String,String> mapping;
}

使用的时候要加@EnableConfigurationProperties(TestConfig .class),不然程序不生效的

@Service
@EnableConfigurationProperties(TestConfig .class)
public class TestService {
	@Resource
	private TestConfig testConfig;
}

就可以通过config正常使用了。

最后补充

  • yml其他格式的解析

    • 代码配置
    app:
    	single: test
    	array: 111,222,333
    	list: 111,222,333
    	mapList: "{
        	key1: '111,222',
         	key2: '333,444',
         	key3: '555,666'
         }"
    
    • 代码:
      	
      /**
       * 默认值是 空字符串,
       * 值是yw
       */
      @Value("${app.single:}")
      private String str1;
      
      
      /**
       * 默认值是空数组 []
       */
      @Value("${app.array:}")
      private String[] array;
      
      /**
       * 默认值是空列表 []
       */
      @Value("${app.list:}")
      private List<String> list;
      
      
      /**
       * 默认值是 null
       */
      @Value("#{${app.mapList:{}}}")
      private Map<String,List<String>> mapList;
      
  • config的pojo配置

        pojo配置其实很简单,就是将属性配置上去即可。

        yml

app:
  datasource:
    mapDatasource:
      TestDatasource1:
        db-type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: ****
        url: ****
        username: ****
        password: ****
        initial-size: 5
        min-idle: 5
        max-active: 10
        max-wait: 5000
        time-between-eviction-runs-millis: 60000
        min-evictable-idle-time-millis: 300000
        max-evictable-idle-time-millis: 900000
        connection-error-retry-attempts: 1
        break-after-acquire-failure: true
      TestDatasource2:
        db-type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: ****
        url: ****
        username: ****
        password: ****
        initial-size: 5
        min-idle: 5
        max-active: 10
        max-wait: 5000
        time-between-eviction-runs-millis: 60000
        min-evictable-idle-time-millis: 300000
        max-evictable-idle-time-millis: 900000
        connection-error-retry-attempts: 1
        break-after-acquire-failure: true

代码就是注意变量名是在yml的配置而已

@Data
@ConfigurationProperties(prefix = "app.datasource")
public class SystemDynamicDatasourceProperties {
    private Map<String, DruidDataSource> mapDatasource;
}