我试图在@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 Type Conversion”7. Validation, Data Binding, and Type Conversion
中所述,这是完全正确的转换器类:
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();
}
}
Spring配置:
<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并采用了一些默认的。
的解决方案: 的 Lotsa感谢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();
}
}
答案 0 :(得分:0)
我认为转换器被忽略,因为你有多个上下文被加载,比如由ContextLoaderListener加载的ROOT上下文和为servlet加载的web应用程序上下文。并且您的配置将被稍后加载的上下文覆盖。
然而,这只是一个猜测,并且无可否认,因为我真的不知道你的应用程序是如何被引导的。
无论如何,查看您的问题,自定义转换器可能不是最简单的解决方案。缓存Key
的整个要点是从参数中创建唯一标识符,以便可以从缓存中提供具有相同参数的后续请求。
因为"'TCDD:stos::'.concat(#stos)"
表达式为key
,所以Spring不得不使用转换器来计算#stos
的字符串值。一个更简单的解决方案是简单地将表达式更改为"'TCDD:stos::'.concat(#stos.toString())"
,并依靠Collections
toString()
方法委托您的类的toString()
方法。 - 从而在参数中创建一个字符串,对于每组参数都是唯一的。
如果您不想实现toString()
,另一个选择是创建一个Class,其唯一责任是将Collection<SimpleTecDocObject> stos
转换为String。在此实用程序类中创建一个静态方法,在此方法中使用您想要的任何键创建逻辑,并从spel
表达式调用它。像T(SimpleTecDocObjectCollectionKeyGenerator).generateKey(stos)
这样的东西。使用此方法,您甚至可以将TCDD:stos::
抽象到密钥生成器实用程序类中。
希望这有帮助。
答案 1 :(得分:0)
谢谢,“#stos.toString()”运行正常。不幸的是,我无法使用KeyGenerator替代方案。
这个不起作用:
"@Cacheable(value = "tecdocData", key = T(SimpleTecDocObjectCollectionKeyGenerator).generateKey(stos)")"
这就是生成器的样子:
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();
}
}
似乎永远不会被召唤。