2013-01-07 10 views
5

Bei der Implementierung stieß ich auf ein Problem mit Spring Cache Abstraction VS-Schnittstellen. Lets sagen, ich habe die folgende Schnittstelle:Spring-Cache-Abstraktion VS-Schnittstellen VS-Schlüsselparameter (Fehler "Nullschlüssel für Cacheoperation zurückgegeben")

package com.example.cache; 

public interface IAddItMethod 
{ 
    Integer addIt(String key); 
} 

Und die beiden folgenden Implementierungen:

package com.example.cache; 

import org.springframework.cache.annotation.Cacheable; 
import org.springframework.stereotype.Component; 

@Component 
public class MethodImplOne implements IAddItMethod 
{ 
    @Override 
    @Cacheable(value="integersPlusOne", key="#keyOne") 
    public Integer addIt(String keyOne) 
    { 
     return new Integer(Integer.parseInt(keyOne) + 1); 
    } 
} 

.

package com.example.cache; 

import org.springframework.cache.annotation.Cacheable; 
import org.springframework.stereotype.Component; 

@Component 
public class MethodImplTwo implements IAddItMethod 
{ 
    @Override 
    @Cacheable(value="integersPlusTwo", key="#keyTwo") 
    public Integer addIt(String keyTwo) 
    { 
     return new Integer(Integer.parseInt(keyTwo) + 2); 
    } 
} 

Beachten Sie, dass das IAddItMethod nicht das ist, das @Cacheable angibt. Wir könnten eine andere Implementierung (ex MethodImplThree) ohne die @Cacheable-Annotation haben.

Wir haben ein einfaches beans.xml bekommen mit:

context:component-scan base-package="com.example.cache" 

das Hinzufügen von zwei jUnit Testfälle:

package com.example.cache; 

import static org.junit.Assert.assertEquals; 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.beans.factory.annotation.Qualifier; 
import org.springframework.test.context.ContextConfiguration; 
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = {"classpath:beans.xml"}) 
public class MethodImplOneTest 
{ 

    @Autowired 
    @Qualifier("methodImplOne") 
    private IAddItMethod classUnderTest; 

    @Test 
    public void testInit() 
    { 
     int number = 1; 
     assertEquals(new Integer(number + 1), classUnderTest.addIt("" + number)); 
    } 

} 

.

package com.example.cache; 

import static org.junit.Assert.assertEquals; 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.beans.factory.annotation.Qualifier; 
import org.springframework.test.context.ContextConfiguration; 
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = {"classpath:beans.xml"}) 
public class MethodImplTwoTest 
{ 

    @Autowired 
    @Qualifier("methodImplTwo") 
    private IAddItMethod classUnderTest; 

    @Test 
    public void testInit() 
    { 
     int number = 1; 
     assertEquals(new Integer(number + 2), classUnderTest.addIt("" + number)); 
    } 

} 

Wenn ich die Tests einzeln ausführen, sind sie erfolgreich. Allerdings, wenn ich sie beide zusammen laufe (Auswahl des Pakets, die rechte Maustaste, läuft as), den zweiten (nicht unbedingt MethodImplTwoTest, nur den zweite Lauf) wird mit folgenden Ausnahme fehlschlagen:

 
java.lang.IllegalArgumentException: Null key returned for cache operation (maybe you are using named params on classes without debug info?) CacheableOperation[public java.lang.Integer com.example.cache.MethodImplOne.addIt(java.lang.String)] caches=[integersPlusOne] | condition='' | key='#keyOne' 
    at org.springframework.cache.interceptor.CacheAspectSupport.inspectCacheables(CacheAspectSupport.java:297) 
    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.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202) 
    at $Proxy16.addIt(Unknown Source) 
    at com.example.cache.ITMethodImplOneIntegrationTest.testInit(ITMethodImplOneIntegrationTest.java:26) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) 
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) 
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) 
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74) 
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83) 
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) 
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) 
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) 
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) 
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) 
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) 
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) 
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) 
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) 
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) 
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) 

Hinweis: Ich verwende Eclipse STS 3.0 und die Option "Variablenattribute zu generierten Klassendateien hinzufügen" ist aktiviert.

WICHTIG: Wenn ich den "Schlüssel" in den @Cacheable Anmerkungen nicht spezifiziere, funktioniert es.

Gibt es irgendetwas, das ich vergessen habe anzugeben? Konfig? Anmerkungen?

Vielen Dank im Voraus!

Antwort

4

Meine Vermutung ist, dass für JDK-Proxy der Parametername aus der Schnittstelle Methode abgerufen wird, so ist es Schlüssel und nicht keyTwo.

Update: Sie können versuchen, Parameter-Indizes zu verwenden, anstatt

aus irgendeinem Grund Wenn die Namen nicht verfügbar sind (zB: keine Debug Informationen), die Parameternamen unter der p auch verfügbar sind < #arg> wobei #arg für den Parameterindex steht (beginnend mit 0).

siehe http://static.springsource.org/spring/docs/3.1.0.M1/spring-framework-reference/html/cache.html#cache-spel-context

+0

Es funktioniert Dank! Ich halte dies jedoch für restriktiv und es ist kompliziert, die Implementierungen zu erzwingen, um das zu respektieren. Ich füge vollständige Javadoc in der Schnittstelle und den aktuellen Implementierungen hinzu, aber ein anderer Entwickler stolpert möglicherweise über dasselbe Problem für eine neue Implementierung. Danke nochmal Boris! – dostiguy

+0

@dostiguy Ich habe einen Link zu der Dokumentation in Bezug auf den Zugriff auf Parameter durch Indizes hinzugefügt, ich habe es selbst nicht versucht –

+0

Gutes Denken, ich hätte vergessen, wir könnten das tun.Ich habe es ausprobiert und meine Tests durchgeführt (echte Implementierung hat ungefähr 100 Testfälle, und die Schlüssel sind komplizierter als das Beispiel) und es funktioniert. Jetzt bin ich einfach nicht sicher, welchen Weg ich bevorzuge. Danke noch einmal. – dostiguy