Spring Cache @Cacheable - 从同一 bean 的另一个方法调用时不工作

2022-08-31 07:18:22

从同一 Bean 的另一个方法调用缓存的方法时,Spring 缓存不起作用。

这里有一个例子,以清晰的方式解释我的问题。

配置:

<cache:annotation-driven cache-manager="myCacheManager" />

<bean id="myCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
    <property name="cacheManager" ref="myCache" />
</bean>

<!-- Ehcache library setup -->
<bean id="myCache"
    class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:shared="true">
    <property name="configLocation" value="classpath:ehcache.xml"></property>
</bean>

<cache name="employeeData" maxElementsInMemory="100"/>  

缓存服务 :

@Named("aService")
public class AService {

    @Cacheable("employeeData")
    public List<EmployeeData> getEmployeeData(Date date){
    ..println("Cache is not being used");
    ...
    }

    public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
        List<EmployeeData> employeeData = getEmployeeData(date);
        ...
    }

}

结果:

aService.getEmployeeData(someDate);
output: Cache is not being used
aService.getEmployeeData(someDate); 
output: 
aService.getEmployeeEnrichedData(someDate); 
output: Cache is not being used

方法调用按预期在第二次调用中使用缓存。但是,当在类(in )中调用该方法时,不会使用缓存。getEmployeeDataemployeeDatagetEmployeeDataAServicegetEmployeeEnrichedData

这是弹簧缓存的工作原理还是我错过了什么?


答案 1

我相信这就是它的工作原理。根据我记得的阅读,有一个生成的代理类,它拦截所有请求并使用缓存值进行响应,但是同一类中的“内部”调用不会获得缓存值。

https://code.google.com/p/ehcache-spring-annotations/wiki/UsingCacheable

只有通过代理传入的外部方法调用才会被截获。这意味着,实际上,目标对象内的方法调用目标对象的另一个方法,即使调用的方法标记为@Cacheable,也不会在运行时导致实际的缓存拦截。


答案 2

从Spring 4.3开始,这个问题可以通过在注释上自动布线来解决:@Resource

@Component
@CacheConfig(cacheNames = "SphereClientFactoryCache")
public class CacheableSphereClientFactoryImpl implements SphereClientFactory {

    /**
     * 1. Self-autowired reference to proxified bean of this class.
     */
    @Resource
    private SphereClientFactory self;

    @Override
    @Cacheable(sync = true)
    public SphereClient createSphereClient(@Nonnull TenantConfig tenantConfig) {
        // 2. call cached method using self-bean
        return self.createSphereClient(tenantConfig.getSphereClientConfig());
    }

    @Override
    @Cacheable(sync = true)
    public SphereClient createSphereClient(@Nonnull SphereClientConfig clientConfig) {
        return CtpClientConfigurationUtils.createSphereClient(clientConfig);
    }
}