2013-08-05 52 views
0

我一個嘗試使用「自己造」對象在@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 

據我所看到的,春天也使試圖轉換HashSet的,但不能轉換載「SimpleTecDocObject」的價值觀。

爲了解決這個問題,我定義了一個轉換器並將其註冊到我的Spring配置中。這樣做是相當筆直向前,如 「7.5春3類型轉換」 7. Validation, Data Binding, and Type Conversion

Converter類記載:

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由於阿克沙伊辛格

代替使用轉換器可以使用使用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

我認爲轉換器被忽略,因爲你有多個上下文被加載,比如一個ContextLoaderListener加載的ROOT上下文和一個爲servlet加載的web應用上下文。並且您的配置將被稍後加載的上下文覆蓋。

但是,這只是一個猜測,並且不是一個非常明智的猜測,因爲我真的不知道你的應用程序是如何引導的。

無論如何,看着你的問題,自定義轉換器可能不是最簡單的解決方案。緩存Key的整個要點是從參數中創建唯一標識符,以便可以從緩存中提供具有相同參數的後續請求。

因爲您有"'TCDD:stos::'.concat(#stos)"作爲您的key表達式,Spring被迫使用轉換器來計算字符串值#stos。一個簡單得多的解決方案是簡單地將您的表達式更改爲"'TCDD:stos::'.concat(#stos.toString())",並依靠CollectionstoString()方法委託您的班級的toString()方法。 - 因此,從參數中創建一個字符串,這對每組參數都是唯一的。

如果您不想實施toString(),另一種選擇是創建一個類,其唯一的責任是將Collection<SimpleTecDocObject> stos轉換爲字符串。在此實用程序類中創建一個靜態方法,使用此方法中所需的任何鍵創建邏輯,並從spel表達式調用它。類似於T(SimpleTecDocObjectCollectionKeyGenerator).generateKey(stos)。使用這種方法,您甚至可以將TCDD:stos::抽象到密鑰生成器實用程序類的內部。

希望這會有所幫助。

+0

謝謝,「#stos.toString()」工作正常。 – user2652217

+0

歡迎您。很高興它對你有效。請接受答案。 – Akshay

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(); 
} 

}

好像它永遠不會被調用。

+0

這很奇怪..T()語法是標準的SpEL ..你有沒有看到任何錯誤?或者只是沒有緩存的方法? – Akshay

+0

該方法沒有被緩存。我將提高net.sf.ehcache和org.springframework.cache的日誌級別,並查看某些調試輸出是否有用於查找問題。 – user2652217

+0

現在我找到了錯誤所在。 首先必須使用全限定類名稱。而且我忘了「#」。看下面的正確聲明: @Cacheable(value =「tecdocData」,key =「T(com.kion.lsg.service.tecdoc.converter.SimpleTecDocObjectCollectionKeyGenerator).generateKey(#stos)」) – user2652217