Spring @Value 加载Map 结构时,报:Cannot convert value

72 阅读1分钟

一:

Spring @Value 加载Map 结构时,报:Cannot convert value of type 'java.util.Collections$UnmodifiableRandomAccessList' to required type 'java.util.Map

org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.util.Collections$UnmodifiableRandomAccessList' to required type 'java.util.Map'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.util.Collections$UnmodifiableRandomAccessList' to required type 'java.util.Map': no matching editors or conversion strategy found
	at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:74)
	at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:54)
	at com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener.resolvePropertyValue(AutoUpdateConfigChangeListener.java:115)
	at com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener.updateSpringValue(AutoUpdateConfigChangeListener.java:90)
	at com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener.onChange(AutoUpdateConfigChangeListener.java:66)
	at com.ctrip.framework.apollo.internals.AbstractConfig$2.run(AbstractConfig.java:440)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266)
	at java.util.concurrent.FutureTask.run(FutureTask.java)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.util.Collections$UnmodifiableRandomAccessList' to required type 'java.util.Map': no matching editors or conversion strategy found
	at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:306)
	at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:125)
	at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:61)
 

二:

Map 配置如下:

@Value("#{${small.flow.by.assign.map:null}}")
private Map<String, String> smallFlowByAssignMap;

small.flow.by.assign.map 配置的值为: { } 或者{} 等, Spring 会将 {} 转换为: List 类型,

注: List 类型的默认值是: {}

如:

@Value("#{${not.execute.new.regulation.List:{}}}")
private List<String> notExecuteNewRegulations;
跟踪源码发现:

org.springframework.expression.spel.standard.InternalSpelExpressionParser#maybeEatInlineListOrMap

@Value 中对应的属性如果值:

  1:开始为 {  后面跟 } 会转换为List:     // empty list '{}'

 2:开始为 {  后面跟 :  会转换为Map     // empty map '{:}'

如果需要使用@Value 声明对象属性为Map,也可以采用间接方式处理:

如 示例代码: 使用属性的set 方法注入属性时,将配置的属性声明为String 类型,然后通过Json 转换为Map类型

` @Value("${small.flow.by.assign.mapStr:{}}")
    public void setSmallFlowByAssignMap(String assignMapStr) {
          this.smallFlowByAssignMap = json().toObject(assignMapStr, new TypeReference<Map<String, Integer>>() {
        });
    }`

三: #{…}和${…} 用法: 参考: www.cnblogs.com/candlia/p/1…

#{…}和{…} {…}用法

通过@Value(“${spelDefault.value}”)可以获取属性文件中对应的值,但是如果属性文件中没有这个属性,则会报错。可以通过赋予默认值解决这个问题,如

@Value("${test.value:127.0.0.1}") #{…}用法 #{…}的{}里面的内容必须符合SpEL表达式,详细的语法,以后可以专门开新的文章介绍,这里只演示简单用法:

{…}和#{…}混合使用 {...}和#{...}可以混合使用,如下文代码执行顺序:通过

${server.name} 从属性文件中获取值并进行替换,然后就变成了 执行SpEL表达式

#{'server1,server2,server3'.split(',')} // SpEL: 传入一个字符串,根据","切分后插入列表中, #{}和配置使用(注意单引号,注意不能反过来{}配置使用(注意单引号,注意不能反过来{}在外面,#{}在里面)

@Value("#{'{server.name}'.split(',')}") private List<String> servers; 在上文中在#{}外面,{}在里面可以执行成功,那么反过来是否可以呢${}在外面,#{}在里面,是不能。

因为spring执行{}是时机要早于#{}。{}在外面,#{}在里面是非法操作。

小结

#{…} 主要用于加载外部属性文件中的值

${…} 用于执行SpEl表达式,并将内容赋值给属性

#{…} 和{…} 可以混合使用,但是必须#{}外面,{}在里面