在实现的过程中,我遇到了Spring缓存抽象VS接口的问题。假设我有以下接口:
package com.example.cache;
public interface IAddItMethod
{
Integer addIt(String key);
}
和以下两个实现:
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);
}
}
注意IAddItMethod不是指定@Cacheable的方法。如果没有@Cacheable注释,我们可以有其他的实现(比如MethodImplThree)。
我们有一个简单的beans.xml,包含:
context:component-scan base-package="com.example.cache"
加上两个jUnit测试用例:
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));
}
}
当我单独运行测试时,它们成功了。但是,如果我同时运行它们(选择包,右键单击,运行为),第二个(不一定是MethodImplTwoTest,只是第二个运行)将失败,并出现以下异常:
<>之前java.lang.IllegalArgumentException:缓存操作返回空键(也许你在没有调试信息的类上使用命名参数?)CacheableOperation[public java.lang.Integer com.example.cache.MethodImplOne.addIt(java.lang.String)] caches=[integersPlusOne] | condition= " | key='#keyOne' "org.springframework.cache.interceptor.CacheAspectSupport.inspectCacheables (CacheAspectSupport.java: 297)org.springframework.cache.interceptor.CacheAspectSupport.execute (CacheAspectSupport.java: 198)org.springframework.cache.interceptor.CacheInterceptor.invoke (CacheInterceptor.java: 66)org.springframework.aop.framework.ReflectiveMethodInvocation.proceed (ReflectiveMethodInvocation.java: 172)org.springframework.aop.framework.JdkDynamicAopProxy.invoke (JdkDynamicAopProxy.java: 202)Proxy16美元。添加剂(未知来源)在com.example.cache.ITMethodImplOneIntegrationTest.testInit (ITMethodImplOneIntegrationTest.java: 26)在sun.reflect.NativeMethodAccessorImpl。invoke0(本地方法)sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java: 39)sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java: 25)java.lang.reflect.Method.invoke (Method.java: 597)在org.junit.runners.model.FrameworkMethod runreflectivecall 1.美元(FrameworkMethod.java: 45)org.junit.internal.runners.model.ReflectiveCallable.run (ReflectiveCallable.java: 15)在org.junit.runners.model.FrameworkMethod.invokeExplosively (FrameworkMethod.java: 42)org.junit.internal.runners.statements.InvokeMethod.evaluate (InvokeMethod.java: 20)org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate (RunBeforeTestMethodCallbacks.java: 74)org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate (RunAfterTestMethodCallbacks.java: 83)org.springframework.test.context.junit4.statements.SpringRepeat.evaluate (SpringRepeat.java: 72)org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild (SpringJUnit4ClassRunner.java: 231)在org.junit.runners.BlockJUnit4ClassRunner.runChild (BlockJUnit4ClassRunner.java: 47)在org.junit.runners.ParentRunner 3.美元运行(ParentRunner.java: 231)在org.junit.runners.ParentRunner 1.美元计划(ParentRunner.java: 60)org.junit.runners.ParentRunner.runChildren (ParentRunner.java: 229)org.junit.runners.ParentRunner.access 000美元(ParentRunner.java: 50)org.junit.runners.ParentRunner评估2.美元(ParentRunner.java: 222)org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate (RunBeforeTestClassCallbacks.java: 61)org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate (RunAfterTestClassCallbacks.java: 71)org.junit.runners.ParentRunner.run (ParentRunner.java: 300)org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run (SpringJUnit4ClassRunner.java: 174)org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run (JUnit4TestReference.java: 50)在org.eclipse.jdt.internal.junit.runner.TestExecution.run (TestExecution.java: 38)org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests (RemoteTestRunner.java: 467)org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests (RemoteTestRunner.java: 683)org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run (RemoteTestRunner.java: 390)org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main (RemoteTestRunner.java: 197)之前注意:我使用的是Eclipse STS 3.0,并且启用了"添加变量属性到生成的类文件"。
重要:如果我没有在@Cacheable注释中指定"key",它就会工作。
有什么我忘了说明的吗?配置吗?注释吗?
提前感谢!
我的猜测是jdk代理的参数名称是从接口方法中获取的,所以它是key
而不是keyTwo
。
update:你可以尝试使用参数索引代替
如果由于某些原因名称不可用(例如:没有调试)信息),参数名称也可以在p<#arg>下找到。其中#arg表示参数索引(从0开始)。
看到http://static.springsource.org/spring/docs/3.1.0.M1/spring-framework-reference/html/cache.html cache-spel-context