Spring 3.1 @Cacheable - 方法仍然执行

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10343885/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 05:06:10  来源:igfitidea点击:

Spring 3.1 @Cacheable - method still executed

springcachingehcache

提问by Andy Miller

I'm trying to implement Spring 3.1 caching as explained hereand here, but it doesn't seem to be working: my method is run through every time even though it is marked @cacheable. What am I doing wrong?

我正在尝试按照此处此处的说明实现 Spring 3.1 缓存,但它似乎不起作用:即使我的方法被标记为 @cacheable,每次都会运行。我究竟做错了什么?

I've moved it into a junit test case with its own configuration file to isolate it from the rest of my application, but the problem still happens. Here are the relevant files:

我已经将它移动到一个带有自己的配置文件的 junit 测试用例中,以将它与我的应用程序的其余部分隔离,但问题仍然发生。以下是相关文件:

Spring-test-servlet.xml

Spring-test-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:cache="http://www.springframework.org/schema/cache"
   xmlns:p="http://www.springframework.org/schema/p"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
<cache:annotation-driven />

<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache"/>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
      p:config-location="classpath:ehcache.xml"/>
</beans>

ehcache.xml

缓存文件

<ehcache>
<diskStore path="java.io.tmpdir"/>
<cache name="cache"
       maxElementsInMemory="100"
       eternal="false"
       timeToIdleSeconds="120"
       timeToLiveSeconds="120"
       overflowToDisk="true"
       maxElementsOnDisk="10000000"
       diskPersistent="false"
       diskExpiryThreadIntervalSeconds="120"
       memoryStoreEvictionPolicy="LRU"/>

</ehcache>

MyTest.java

我的测试程序

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring-test-servlet.xml"})
@Component
public class MyTest extends TestCase {

    @Test
    public void testCache1(){
        for(int i = 0; i < 5; i++){
            System.out.println("Calling someMethod...");
            System.out.println(someMethod(0));
        }
    }

    @Cacheable("testmethod")
    private int someMethod(int val){
        System.out.println("Not from cache");
        return 5;
    }
}

Relevant Pom entries: (spring-version = 3.1.1.RELEASE)

相关 Pom 条目:(spring-version = 3.1.1.RELEASE)

    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache-core</artifactId>
        <version>2.5.1</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>

when I run the test, Spring puts out some debug messages that looks like my cache is initialized without errors

当我运行测试时,Spring 会发出一些调试消息,看起来我的缓存已初始化且没有错误

DEBUG: config.ConfigurationHelper - No CacheManagerEventListenerFactory class specified. Skipping...
DEBUG: ehcache.Cache - No BootstrapCacheLoaderFactory class specified. Skipping...
DEBUG: ehcache.Cache - CacheWriter factory not configured. Skipping...
DEBUG: config.ConfigurationHelper - No CacheExceptionHandlerFactory class specified. Skipping...
DEBUG: store.MemoryStore - Initialized net.sf.ehcache.store.MemoryStore for cache
DEBUG: disk.DiskStorageFactory - Failed to delete file cache.data
DEBUG: disk.DiskStorageFactory - Failed to delete file cache.index
DEBUG: disk.DiskStorageFactory - Matching data file missing (or empty) for index file. Deleting index file /var/folders/qg/xwdvsg6x3mx_z_rcfvq7lc0m0000gn/T/cache.index
DEBUG: disk.DiskStorageFactory - Failed to delete file cache.index
DEBUG: ehcache.Cache - Initialised cache: cache
DEBUG: config.ConfigurationHelper - CacheDecoratorFactory not configured. Skipping for 'cache'.
DEBUG: config.ConfigurationHelper - CacheDecoratorFactory not configured for defaultCache. Skipping for 'cache'.

but the debug output shows no cache checks between method calls to someMethod and the print statement from inside someMethod prints every time.

但是调试输出显示在对 someMethod 的方法调用之间没有缓存检查,并且每次都打印 someMethod 内部的打印语句。

Is there something I'm missing?

有什么我想念的吗?

回答by Lee Chee Kiam

From http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/cache.html

来自http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/cache.html

In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual cachingat runtime even if the invoked method is marked with @Cacheable - considering using the aspectj mode in this case.

在代理模式下(默认),只有通过代理进入的外部方法调用才会被拦截。这意味着自调用,实际上,目标对象中的一个方法调用目标对象的另一个方法,即使被调用的方法被标记为@Cacheable ,也不会在运行时导致实际缓存- 考虑使用aspectj模式这个案例。

and

Method visibility and @Cacheable/@CachePut/@CacheEvict

When using proxies, you should apply the @Cacheannotations only to methods with public visibility.

方法可视性和@Cacheable/ @CachePut/@CacheEvict

使用代理时,您应该仅将@Cache注释应用于具有公共可见性的方法。

  1. You self-invoke someMethodin the same target object.
  2. Your @Cacheablemethod is not public.
  1. someMethod在同一个目标对象中自调用。
  2. 你的@Cacheable方法不是公开的。

回答by pap

You need to define a cache that matches the name you are referencing in you annotation ("testmethod"). Create an entry in your ehcache.xml for that cache as well.

您需要定义一个与您在注释中引用的名称相匹配的缓存(“testmethod”)。在您的 ehcache.xml 中也为该缓存创建一个条目。

回答by Mario Eis

In addition to Lee Chee Kiam: Here is my solution for small projects with only marginal usage of bypassing (not annotated) method calls. The DAO is simply injected into itself as a proxy and calls it's own methods using that proxy instead of a simple method call.So @Cacheable is considered without doing complicated insturmentation.

除了 Lee Chee Kiam:这里是我的解决方案,适用于仅少量使用绕过(未注释)方法调用的小型项目。DAO 只是作为代理注入到自身中,并使用该代理而不是简单的方法调用来调用它自己的方法。所以@Cacheable 被认为没有做复杂的工具。

In-code documentation is strongly advidsed, as it may look strange to colleagues. But its easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd also advice the AspectJ solution as Lee Chee Kiam did.

强烈建议使用代码内文档,因为它对同事来说可能看起来很奇怪。但它易于测试、简单、快速实现,并且让我无需使用完整的 AspectJ 工具。然而,对于更频繁的使用,我也会像 Lee Chee Kiam 那样建议 AspectJ 解决方案。

@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {

    private final PersonDao _personDao;

    @Autowired
    public PersonDao(PersonDao personDao) {
        _personDao = personDao;
    }

    @Cacheable(value = "defaultCache", key = "#id")
    public Person findPerson(int id) {
        return getSession().getPerson(id);
    }

    public List<Person> findPersons(int[] ids) {
        List<Person> list = new ArrayList<Person>();
        for (int id : ids) {
            list.add(_personDao.findPerson(id));
        }
        return list;
    }
}