Evaluate @Cacheable(condition) once per method invocation only
Issue: SPR-17024
This commit is contained in:
parent
93d91219fd
commit
faef363e85
|
|
@ -693,6 +693,9 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
|||
|
||||
private final Collection<String> cacheNames;
|
||||
|
||||
@Nullable
|
||||
private Boolean conditionPassing;
|
||||
|
||||
public CacheOperationContext(CacheOperationMetadata metadata, Object[] args, Object target) {
|
||||
this.metadata = metadata;
|
||||
this.args = extractArgs(metadata.method, args);
|
||||
|
|
@ -733,12 +736,17 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
|||
}
|
||||
|
||||
protected boolean isConditionPassing(@Nullable Object result) {
|
||||
if (StringUtils.hasText(this.metadata.operation.getCondition())) {
|
||||
EvaluationContext evaluationContext = createEvaluationContext(result);
|
||||
return evaluator.condition(this.metadata.operation.getCondition(),
|
||||
this.metadata.methodKey, evaluationContext);
|
||||
if (this.conditionPassing == null) {
|
||||
if (StringUtils.hasText(this.metadata.operation.getCondition())) {
|
||||
EvaluationContext evaluationContext = createEvaluationContext(result);
|
||||
this.conditionPassing = evaluator.condition(this.metadata.operation.getCondition(),
|
||||
this.metadata.methodKey, evaluationContext);
|
||||
}
|
||||
else {
|
||||
this.conditionPassing = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return this.conditionPassing;
|
||||
}
|
||||
|
||||
protected boolean canPutToCache(@Nullable Object value) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -21,6 +21,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.CacheTestUtils;
|
||||
|
|
@ -33,7 +34,10 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.cache.CacheTestUtils.*;
|
||||
|
||||
/**
|
||||
|
|
@ -45,6 +49,7 @@ public class EnableCachingIntegrationTests {
|
|||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
|
||||
@After
|
||||
public void closeContext() {
|
||||
if (this.context != null) {
|
||||
|
|
@ -52,6 +57,7 @@ public class EnableCachingIntegrationTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void fooServiceWithInterface() {
|
||||
this.context = new AnnotationConfigApplicationContext(FooConfig.class);
|
||||
|
|
@ -77,22 +83,48 @@ public class EnableCachingIntegrationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void beanCondition() {
|
||||
public void beanConditionOff() {
|
||||
this.context = new AnnotationConfigApplicationContext(BeanConditionConfig.class);
|
||||
Cache cache = getCache();
|
||||
FooService service = this.context.getBean(FooService.class);
|
||||
Cache cache = getCache();
|
||||
|
||||
Object key = new Object();
|
||||
service.getWithCondition(key);
|
||||
assertCacheMiss(key, cache);
|
||||
service.getWithCondition(key);
|
||||
assertCacheMiss(key, cache);
|
||||
|
||||
assertEquals(2, this.context.getBean(BeanConditionConfig.Bar.class).count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanConditionOn() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.setEnvironment(new MockEnvironment().withProperty("bar.enabled", "true"));
|
||||
ctx.register(BeanConditionConfig.class);
|
||||
ctx.refresh();
|
||||
this.context = ctx;
|
||||
|
||||
FooService service = this.context.getBean(FooService.class);
|
||||
Cache cache = getCache();
|
||||
|
||||
Object key = new Object();
|
||||
Object value = service.getWithCondition(key);
|
||||
assertCacheHit(key, value, cache);
|
||||
value = service.getWithCondition(key);
|
||||
assertCacheHit(key, value, cache);
|
||||
|
||||
assertEquals(2, this.context.getBean(BeanConditionConfig.Bar.class).count);
|
||||
}
|
||||
|
||||
private Cache getCache() {
|
||||
return this.context.getBean(CacheManager.class).getCache("testCache");
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class SharedConfig extends CachingConfigurerSupport {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
|
|
@ -100,34 +132,42 @@ public class EnableCachingIntegrationTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Import(SharedConfig.class)
|
||||
@EnableCaching
|
||||
static class FooConfig {
|
||||
|
||||
@Bean
|
||||
public FooService fooService() {
|
||||
return new FooServiceImpl();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Import(SharedConfig.class)
|
||||
@EnableCaching(proxyTargetClass = true)
|
||||
static class FooConfigCglib {
|
||||
|
||||
@Bean
|
||||
public FooService fooService() {
|
||||
return new FooServiceImpl();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private interface FooService {
|
||||
|
||||
Object getSimple(Object key);
|
||||
|
||||
Object getWithCondition(Object key);
|
||||
}
|
||||
|
||||
|
||||
@CacheConfig(cacheNames = "testCache")
|
||||
private static class FooServiceImpl implements FooService {
|
||||
|
||||
private final AtomicLong counter = new AtomicLong();
|
||||
|
||||
@Override
|
||||
|
|
@ -143,17 +183,25 @@ public class EnableCachingIntegrationTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@Import(FooConfig.class)
|
||||
@EnableCaching
|
||||
static class BeanConditionConfig {
|
||||
|
||||
@Autowired
|
||||
Environment env;
|
||||
|
||||
@Bean
|
||||
public Bar bar() {
|
||||
return new Bar(false);
|
||||
return new Bar(Boolean.valueOf(env.getProperty("bar.enabled")));
|
||||
}
|
||||
|
||||
|
||||
static class Bar {
|
||||
|
||||
public int count;
|
||||
|
||||
private final boolean enabled;
|
||||
|
||||
public Bar(boolean enabled) {
|
||||
|
|
@ -161,6 +209,7 @@ public class EnableCachingIntegrationTests {
|
|||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
this.count++;
|
||||
return this.enabled;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue