我试图在 @Cacheable 注释中使用“自己制作的”对象作为键的一部分:
@Cacheable(value = "tecdocData", key = "'TCDD:stos::'.concat(#stos)")
List<TecDocData> getTecDocData(Collection<SimpleTecDocObject> stos);
这会导致以下堆栈跟踪(相关部分)出错:
Caused by: org.springframework.expression.AccessException: Problem invoking method: public java.lang.String java.lang.String.concat(java.lang.String)
at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:73)
at org.springframework.expression.spel.ast.MethodReference$MethodValueRef.getValue(MethodReference.java:97)
... 100 more
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1001E:(pos 0): Type conversion problem, cannot convert from java.util.HashSet<?> to java.lang.String
at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:72)
at org.springframework.expression.spel.support.ReflectionHelper.convertArguments(ReflectionHelper.java:281)
at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:61)
... 101 more
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.util.HashSet<?> to type java.lang.String for value '[SimpleTecDocObject [guid=GUID-C2DBD976-79F0-42FB-94CE-9352DE43A184, locale=de, version=1]]'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type com.sxp.setix.api.SimpleTecDocObject to type java.lang.String
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:169)
at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:66)
... 103 more
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type com.sxp.setix.api.SimpleTecDocObject to type java.lang.String
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:276)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:172)
at org.springframework.core.convert.support.CollectionToStringConverter.convert(CollectionToStringConverter.java:65)
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:35)
... 105 more
据我所见,spring 确实尝试转换 HashSet 但未能转换包含的“SimpleTecDocObject”值。
为了解决这个问题,我定义了一个转换器并将其注册到我的 Spring 配置中。如“7.5 Spring 3 类型转换” 7. 验证、数据绑定和类型转换中所述,这是非常直接地完成的
转换器类:
public class SimpleTecdocObjectConverter implements Converter<SimpleTecDocObject, String> {
@Override
public String convert(final SimpleTecDocObject source) {
StringBuilder result = new StringBuilder();
result.append(source.getGuid()).append(source.getLocale()).append(source.getVersion());
return result.toString();
}
}
弹簧配置:
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean id="tdConverter" class="com.kion.lsg.service.tecdoc.converter.SimpleTecdocObjectConverter" />
</list>
</property>
</bean>
不幸的是,这似乎不起作用。我得到与上面完全相同的异常。Spring抽象似乎忽略了上面定义的conversionService并采用了一些默认值。
解决方案: 感谢 Akshay Singhal
可以在 SpEL 表达式中使用静态密钥生成器,而不是使用转换器:
@Cacheable(value = "tecdocData", key = "T(com.kion.lsg.service.tecdoc.converter.SimpleTecDocObjectCollectionKeyGenerator).generateKey(#stos)")
List<TecDocData> getTecDocData(Collection<SimpleTecDocObject> stos);
例如 KeyGenerator 定义如下:
public class SimpleTecDocObjectCollectionKeyGenerator {
public static String generateKey(final Collection<SimpleTecDocObject> stos){
List<String> tecdocIdsList = new ArrayList<>();
for (SimpleTecDocObject sto : stos){
tecdocIdsList.add(sto.toString());
}
Collections.sort(tecdocIdsList);
return tecdocIdsList.toString();
}
}