2014-02-17 64 views
3

我想在延伸的findById(長ID)方法相同的AbstractService多個服務啓用緩存。@Cacheable - 如何生成唯一緩存鍵

所以在我的applicationContext我寫道:

<!-- cache definitions --> 
    <cache:advice id="cacheAdvice" cache-manager="cacheManager"> 
     <cache:caching cache="refs"> 
       <cache:cacheable method="findById" key="#root.targetClass + #id"/> 
     </cache:caching> 
    </cache:advice> 

    <aop:config> 
     <aop:advisor advice-ref="cacheAdvice" pointcut="execution(* x.y.*.service.reference.*.*(..))"/> 
    </aop:config> 

問題是我希望能夠生成用於在方法findById每個服務呼叫的唯一關鍵,因爲ID可以是相同的(因此有一個類鑄件除外):

java.lang.ClassCastException: x.y.model.RefSituation cannot be cast to x.y.model.RefCivility 

單元測試:

public class AbstractReferenceServiceTest extends AbstractBiTest { 

    @Inject 
    @Named("refSituationServiceClient") 
    private RefSituationService refSituationService; 

    @Inject 
    @Named("refCivilityServiceClient") 
    private RefCivilityService refCivilityService; 

    @Test 
    public void findById() { 
     RefSituation situation = refSituationService.findById(1L); 
     situation = refSituationService.findById(2L); 
     situation = refSituationService.findById(1L); 

     RefCivility refCivility = refCivilityService.findById(1L); 
     refCivility = refCivilityService.findById(2L); 
     refCivility = refCivilityService.findById(1L); 
    } 
} 

兩個Services電子xtends一個AbstractReferenceService:

public interface RefSituationService extends AbstractReferenceService<RefSituation> {} 
public interface RefCivilityService extends AbstractReferenceService<RefCivility> {} 

而且AbstractReferenceService延伸通過稱爲RestHub(https://github.com/resthub/resthub-spring-stack/blob/master/resthub-common/src/main/java/org/resthub/common/service/CrudService.java

框架但與配置提供了一種crudService上述我有一個錯誤:

org.springframework.expression.spel.SpelEvaluationException: EL1030E:(pos 0): The operator 'ADD' is not supported between objects of type 'java.lang.Class' and 'null' 
    at org.springframework.expression.spel.ExpressionState.operate(ExpressionState.java:198) 
    at org.springframework.expression.spel.ast.OpPlus.getValueInternal(OpPlus.java:97) 
    at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:93) 
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:89) 
    at org.springframework.cache.interceptor.ExpressionEvaluator.key(ExpressionEvaluator.java:80) 
    at org.springframework.cache.interceptor.CacheAspectSupport$CacheOperationContext.generateKey(CacheAspectSupport.java:464) 
    at org.springframework.cache.interceptor.CacheAspectSupport.inspectCacheables(CacheAspectSupport.java:291) 
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:198) 
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:66) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) 
    at com.sun.proxy.$Proxy175.findById(Unknown Source) 

預先感謝你的幫助。

+0

目前還不清楚其RefSituation接口或RefCivility接口是否導致了問題。你可以發佈兩者的代碼嗎?我還建議將測試分爲兩個獨立的測試用例來隔離問題。 –

+0

喜彼得我有原來的職位感謝更新 –

回答

2

的問題是#root.targetClass.name總是 「CrudService」,來解決你的問題:

1-實現自己CacheKeyGenerator:

的applicationContext.xml :

<bean id="refCacheKeyGenerator" class="x.y.cache.RefCacheKeyGenerator" /> 

<!-- cache definitions --> 
    <cache:advice id="cacheAdvice" key-generator="refCacheKeyGenerator" cache-manager="cacheManager"> 
     <cache:caching cache="refs"> 
       <cache:cacheable method="findById"/> 
     </cache:caching> 
    </cache:advice> 

    <aop:config> 
     <aop:advisor advice-ref="cacheAdvice" pointcut="execution(* x.y.*.service.reference.*.*(..))"/> 
    </aop:config> 

的Java:

public class RefCacheKeyGenerator implements org.springframework.cache.interceptor.KeyGenerator { 

    @Override 
    public Object generate(Object target, Method method, Object... params) { 
     final List<Object> key = new ArrayList<>(); 

     key.add(method.getDeclaringClass().getName());  
     key.add(method.getName()); 

     List<Class<?>> clazz = ClassUtils.getAllInterfaces(target.getClass()); 
     if(CollectionUtils.isNotEmpty(clazz)){ 
      for(Class<?> sClass : clazz){ 
       if(AbstractReferenceService.class.isAssignableFrom(sClass)){ 
        if(!AbstractReferenceService.class.equals(sClass)){ 
        key.add(sClass.getName()); 
        } 
       } 
      } 
     } 
     for (final Object o : params) { 
      key.add(o); 
     } 

     return key; 
    } 

} 

測試:

public class RefCacheTest extends AbstractTest { 

    @Autowired 
    private RefSituationService refSituationService; 

    @Autowired 
    private RefCivilityService refCivilityService; 

    @Autowired 
    private CacheManager cacheManager; 


    @Test 
    public void findById() { 

     Cache refCache = cacheManager.getCache(MyCache.REFS); 
     refCache.setStatisticsEnabled(true); 

     assertThat(refSituationService.findById(1L)).isInstanceOf(RefSituation.class); 
     assertThat(refSituationService.findById(1L)).isInstanceOf(RefSituation.class); 
     assertThat(refSituationService.findById(2L)).isInstanceOf(RefSituation.class); 

     assertThat(refCivilityService.findById(1L)).isInstanceOf(RefCivility.class); 
     assertThat(refCivilityService.findById(1L)).isInstanceOf(RefCivility.class); 
     assertThat(refCivilityService.findById(2L)).isInstanceOf(RefCivility.class); 

     System.out.println(refCache.getName() +" - "+ refCache.getStatistics().toString()); 

     assertThat(refCache.getStatistics().getCacheHits()).isEqualTo(2); 
     assertThat(refCache.getSize()).isEqualTo(4); 
    } 
0

最有可能發生什麼事情是+運營商和類型的問題,可能是也可能不是Strings。假設你想字符串連接(長算法將導致衝突,我認爲你想避免的),強迫你的關鍵參數Strings可能會解決問題。阿拉:

<!-- cache definitions --> 
<cache:advice id="cacheAdvice" cache-manager="cacheManager"> 
    <cache:caching cache="refs"> 
      <cache:cacheable method="findById" key="#root.targetClass.name + #id.toString()"/> 
    </cache:caching> 
</cache:advice> 
+0

同樣的問題上面的代碼:對空上下文對象試圖調用toString()方法:org.springframework.expression.spel.SpelEvaluationException:EL1011E:(POS 29):方法調用 –

0

現在我已經嘗試它煤層只是一個配置工作,沒有密鑰生成的額外定製的實現:

@Cacheable(value = "lookups", key="T(org.springframework.cache.interceptor.SimpleKeyGenerator).generateKey(#root.target.class, #root.args)") 

主要思想包括在這裏具體類的對象作爲關鍵部分之一。