Spring Cache @Cacheable - not working while calling from another method of the same bean

后端 未结 9 1426
执念已碎
执念已碎 2020-11-27 10:06

Spring cache is not working when calling cached method from another method of the same bean.

Here is an example to explain my problem in clear way.<

相关标签:
9条回答
  • 2020-11-27 10:48

    Since Spring 4.3 the problem could be solved using self-autowiring over @Resource annotation:

    @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);
        }
    }
    
    0 讨论(0)
  • 2020-11-27 10:48

    the easiest solution by far is just to reference like this:

    AService.this.getEmployeeData(date);
    
    0 讨论(0)
  • 2020-11-27 10:54

    Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advidsed, as it may look strage 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 advice the AspectJ solution.

    @Service
    @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
    class AService {
    
        private final AService _aService;
    
        @Autowired
        public AService(AService aService) {
            _aService = aService;
        }
    
        @Cacheable("employeeData")
        public List<EmployeeData> getEmployeeData(Date date){
            ..println("Cache is not being used");
            ...
        }
    
        public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
            List<EmployeeData> employeeData = _aService.getEmployeeData(date);
            ...
        }
    }
    
    0 讨论(0)
  • 2020-11-27 10:59

    I use internal inner bean (FactoryInternalCache) with real cache for this purpose:

    @Component
    public class CacheableClientFactoryImpl implements ClientFactory {
    
    private final FactoryInternalCache factoryInternalCache;
    
    @Autowired
    public CacheableClientFactoryImpl(@Nonnull FactoryInternalCache factoryInternalCache) {
        this.factoryInternalCache = factoryInternalCache;
    }
    
    /**
     * Returns cached client instance from cache.
     */
    @Override
    public Client createClient(@Nonnull AggregatedConfig aggregateConfig) {
        return factoryInternalCache.createClient(aggregateConfig.getClientConfig());
    }
    
    /**
     * Returns cached client instance from cache.
     */
    @Override
    public Client createClient(@Nonnull ClientConfig clientConfig) {
        return factoryInternalCache.createClient(clientConfig);
    }
    
    /**
     * Spring caching feature works over AOP proxies, thus internal calls to cached methods don't work. That's why
     * this internal bean is created: it "proxifies" overloaded {@code #createClient(...)} methods
     * to real AOP proxified cacheable bean method {@link #createClient}.
     *
     * @see <a href="https://stackoverflow.com/questions/16899604/spring-cache-cacheable-not-working-while-calling-from-another-method-of-the-s">Spring Cache @Cacheable - not working while calling from another method of the same bean</a>
     * @see <a href="https://stackoverflow.com/questions/12115996/spring-cache-cacheable-method-ignored-when-called-from-within-the-same-class">Spring cache @Cacheable method ignored when called from within the same class</a>
     */
    @EnableCaching
    @CacheConfig(cacheNames = "ClientFactoryCache")
    static class FactoryInternalCache {
    
        @Cacheable(sync = true)
        public Client createClient(@Nonnull ClientConfig clientConfig) {
            return ClientCreationUtils.createClient(clientConfig);
        }
    }
    }
    
    0 讨论(0)
  • 2020-11-27 11:03

    The example below is what I use to hit the proxy from within the same bean, it is similar to @mario-eis' solution, but I find it a bit more readable (maybe it's not:-). Anyway, I like to keep the @Cacheable annotations at the service level:

    @Service
    @Transactional(readOnly=true)
    public class SettingServiceImpl implements SettingService {
    
    @Inject
    private SettingRepository settingRepository;
    
    @Inject
    private ApplicationContext applicationContext;
    
    @Override
    @Cacheable("settingsCache")
    public String findValue(String name) {
        Setting setting = settingRepository.findOne(name);
        if(setting == null){
            return null;
        }
        return setting.getValue();
    }
    
    @Override
    public Boolean findBoolean(String name) {
        String value = getSpringProxy().findValue(name);
        if (value == null) {
            return null;
        }
        return Boolean.valueOf(value);
    }
    
    /**
     * Use proxy to hit cache 
     */
    private SettingService getSpringProxy() {
        return applicationContext.getBean(SettingService.class);
    }
    ...
    

    See also Starting new transaction in Spring bean

    0 讨论(0)
  • 2020-11-27 11:04

    I believe this is how it works. From what I remember reading, there is a proxy class generated that intercepts all requests and responds with the cached value, but 'internal' calls within the same class will not get the cached value.

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

    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 cache interception at runtime even if the invoked method is marked with @Cacheable.

    0 讨论(0)
提交回复
热议问题