我正在编写一些代码以允许使用Spring Expression Language进行动态属性更改。我传入一个bean名称,属性名称和新值的表达式,所有字符串。
这适用于string,int,boolean和list类型的属性。我无法获得地图属性。我查看了SPeL文档,包括示例,但我没有看到我正在做的事情有什么问题。我得到的例外没有帮助。
忽略try / catch块,基本代码就是这样:
ExpressionParser parser = new SpelExpressionParser();
Expression parsedPropertyNameExpression = parser.parseExpression(propertyName);
SimpleEvaluationContext evalContext = SimpleEvaluationContext.forReadWriteDataBinding().build();
Object currentValue = parsedPropertyNameExpression.getValue(evalContext, bean);
parsedPropertyNameExpression.setValue(evalContext, bean, expression);
当我的“表达式”是“789,0123,345”并且我正在设置的属性是List时,这完全正常。
但是,当我设置Map(“”)类型的属性时,表达式值为“{abc:'def',ghi:'jkl'}”,我得到以下异常:
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.util.Map<java.lang.String, java.lang.String>]
我尝试过该表达式字符串的不同变体,结果基本相同。
更新:
我注意到以下SO帖子:How to inject a Map using the @Value Spring Annotation?。
其中一个未被接受的答案提到在属性中定义Map并使用@Value注释注入它,我认为这是使用类似的机制。我怎么能在代码中这样做?
我得到的例外没有帮助。
没有找到能够从类型[java.lang.String]转换为类型[java.util.Map]的转换器
对我来说似乎很清楚。
没有内置支持将地图的字符串表示转换为Map
对象。
您可以注册自定义函数,或在SpEL表达式中使用Jackson ObjectMapper
bean引用。
编辑
这是一种方法(使用杰克逊的自定义Converter
)...
public class So55485198Application {
public static void main(String[] args) {
Bean bean = new Bean();
getAndSet("list", bean, "abc, def");
getAndSet("map", bean, "{'abc':'def'}");
}
public static void getAndSet(String propertyName, Bean bean, String expression) {
ExpressionParser parser = new SpelExpressionParser();
Expression parsedPropertyNameExpression = parser.parseExpression(propertyName);
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToMapConverter());
SimpleEvaluationContext evalContext = SimpleEvaluationContext.forReadWriteDataBinding()
.withConversionService(conversionService)
.build();
Object currentValue = parsedPropertyNameExpression.getValue(evalContext, bean);
System.out.println("old:" + currentValue);
parsedPropertyNameExpression.setValue(evalContext, bean, expression);
System.out.println("new:" + parsedPropertyNameExpression.getValue(evalContext, bean));
}
static class StringToMapConverter implements Converter<String, Map<String, String>> {
private static final ObjectMapper objectMapper = new ObjectMapper();
static {
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
}
@SuppressWarnings("unchecked")
@Override
public Map<String, String> convert(String source) {
try {
return this.objectMapper.readValue(source, LinkedHashMap.class);
}
catch (IOException e) {
e.printStackTrace();
throw new IllegalStateException(e);
}
}
}
static class Bean {
private List<String> list = new ArrayList<>(Arrays.asList("foo", "bar"));
private Map<String, String> map = new HashMap<>(Collections.singletonMap("foo", "bar"));
public List<String> getList() {
return this.list;
}
public void setList(List<String> list) {
this.list = list;
}
public Map<String, String> getMap() {
return this.map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
}
}