繁体   English   中英

Spring Boot EnableCaching和Cacheable注释无效

[英]Spring Boot EnableCaching and Cacheable annotation not working

我想将主数据缓存到Redis。

所以,我写了这些代码。

@Configuration
@EnableCaching
public class AppConfig extends CachingConfigurerSupport {
    @Bean
    @Autowired
    public CacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
        Map<String, Long> expires = new HashMap<>();
        expires.put("cache.day", new Long(24 * 60 * 60));
        cacheManager.setExpires(expires);
        return cacheManager;
    }
}

package com.taisho.artifacts.repository.impl;

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

import java.util.ArrayList;
import java.util.List;

@Repository
public class TestRepository {

    @Cacheable(value = "cache.day", key = "'cache.test'")
    public List<String> getTest() {
        List<String> list = new ArrayList<>();
        list.add("test");
        list.add("sample");
        return list;
    }

    public void printTest() {
        System.out.println(getTest());
    }
}

和ymlfile

spring:
  redis:
    host: 127.0.0.1
    port: 26379

但是,缓存无法正常工作......

每当我调用printTest方法时,“getTest”方法都会执行。 Redis没有数据......我的代码中有什么问题?

注意

SpringBoot版本是1.4.0

依赖是

compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
compile("org.springframework.boot:spring-boot-starter-data-redis:${springBootVersion}")
compile("org.springframework.boot:spring-boot-autoconfigure:${springBootVersion}")

TL; DR

Spring AOP是基于代理的 ,所以当你调用getTest()从方法printTest()方法中, getTest()方法将在调用this引用不被代理的版本,这是能够执行高速缓存操作的。 通常这是一种设计气味 ,您最好重新考虑当前的设计。 但作为一种解决方法,您可以使用AopContext

public void printTest() {
    System.out.println(((TestRepository) AopContext.currentProxy()).getTest());
}

详细解答

假设您有一个客户端代码,可以通过依赖注入访问TestRepository

@Component
class SomeUnfortunateClient {
    // I know field injection is evil!
    @Autowired TestRepository testRepository;

    void youAreGoingToBeSurprised() {
        testRepository.printTest();
    }
}

TestRepository是一个Spring托管存储库,为了向TestRepository添加额外的功能,例如Caching,Spring将为它创建一个Proxy 这意味着对testRepository对象引用的方法调用将是对代理的调用,因此代理将能够委托testRepository该特定方法调用相关的所有拦截器(通知)。 在您的情况下,这些建议将检查缓存条目是否存在。

但是,一旦调用最终到达目标对象,在这种情况下, TestRepository引用,它可以调用它自己的任何方法,例如System.out.println(getTest()); ,将针对this引用而不是代理调用。 这意味着自我调用不会导致与方法调用相关的建议有机会执行。

正如Spring文档所述:

好的,那么该怎么办呢? 最好的方法(这里松散地使用最好的术语)是重构代码,以便不会发生自我调用。 当然,这确实需要你做一些工作,但这是最好的,最少侵入性的方法。 接下来的方法是绝对可怕的,我几乎要谨慎地指出它,因为它是如此可怕。 你可以(窒息!)通过这样做完全将你的类中的逻辑绑定到Spring AOP:

public class SimplePojo implements Pojo {

    public void foo() {
        // this works, but... gah!
        ((Pojo) AopContext.currentProxy()).bar();
    }

    public void bar() {
        // some logic...
    }
}

这完全将您的代码耦合到Spring AOP,它使类本身意识到它正在AOP上下文中使用,它在AOP面前飞行。 在创建代理时,还需要一些额外的配置。

进一步阅读

这个答案很大程度上基于Spring Documentation,所以对于(甚至!)更详细的讨论,你一定要查看Spring Documentation中的Understanding AOP proxyies部分。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM