Skip to content

Commit faef363

Browse files
committed
Evaluate @Cacheable(condition) once per method invocation only
Issue: SPR-17024
1 parent 93d9121 commit faef363

File tree

2 files changed

+66
-9
lines changed

2 files changed

+66
-9
lines changed

spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,9 @@ protected class CacheOperationContext implements CacheOperationInvocationContext
693693

694694
private final Collection<String> cacheNames;
695695

696+
@Nullable
697+
private Boolean conditionPassing;
698+
696699
public CacheOperationContext(CacheOperationMetadata metadata, Object[] args, Object target) {
697700
this.metadata = metadata;
698701
this.args = extractArgs(metadata.method, args);
@@ -733,12 +736,17 @@ private Object[] extractArgs(Method method, Object[] args) {
733736
}
734737

735738
protected boolean isConditionPassing(@Nullable Object result) {
736-
if (StringUtils.hasText(this.metadata.operation.getCondition())) {
737-
EvaluationContext evaluationContext = createEvaluationContext(result);
738-
return evaluator.condition(this.metadata.operation.getCondition(),
739-
this.metadata.methodKey, evaluationContext);
739+
if (this.conditionPassing == null) {
740+
if (StringUtils.hasText(this.metadata.operation.getCondition())) {
741+
EvaluationContext evaluationContext = createEvaluationContext(result);
742+
this.conditionPassing = evaluator.condition(this.metadata.operation.getCondition(),
743+
this.metadata.methodKey, evaluationContext);
744+
}
745+
else {
746+
this.conditionPassing = true;
747+
}
740748
}
741-
return true;
749+
return this.conditionPassing;
742750
}
743751

744752
protected boolean canPutToCache(@Nullable Object value) {

spring-context/src/test/java/org/springframework/cache/config/EnableCachingIntegrationTests.java

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121
import org.junit.After;
2222
import org.junit.Test;
2323

24+
import org.springframework.beans.factory.annotation.Autowired;
2425
import org.springframework.cache.Cache;
2526
import org.springframework.cache.CacheManager;
2627
import org.springframework.cache.CacheTestUtils;
@@ -33,7 +34,10 @@
3334
import org.springframework.context.annotation.Bean;
3435
import org.springframework.context.annotation.Configuration;
3536
import org.springframework.context.annotation.Import;
37+
import org.springframework.core.env.Environment;
38+
import org.springframework.mock.env.MockEnvironment;
3639

40+
import static org.junit.Assert.*;
3741
import static org.springframework.cache.CacheTestUtils.*;
3842

3943
/**
@@ -45,13 +49,15 @@ public class EnableCachingIntegrationTests {
4549

4650
private ConfigurableApplicationContext context;
4751

52+
4853
@After
4954
public void closeContext() {
5055
if (this.context != null) {
5156
this.context.close();
5257
}
5358
}
5459

60+
5561
@Test
5662
public void fooServiceWithInterface() {
5763
this.context = new AnnotationConfigApplicationContext(FooConfig.class);
@@ -77,57 +83,91 @@ private void fooGetSimple(FooService service) {
7783
}
7884

7985
@Test
80-
public void beanCondition() {
86+
public void beanConditionOff() {
8187
this.context = new AnnotationConfigApplicationContext(BeanConditionConfig.class);
82-
Cache cache = getCache();
8388
FooService service = this.context.getBean(FooService.class);
89+
Cache cache = getCache();
8490

8591
Object key = new Object();
8692
service.getWithCondition(key);
8793
assertCacheMiss(key, cache);
94+
service.getWithCondition(key);
95+
assertCacheMiss(key, cache);
96+
97+
assertEquals(2, this.context.getBean(BeanConditionConfig.Bar.class).count);
98+
}
99+
100+
@Test
101+
public void beanConditionOn() {
102+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
103+
ctx.setEnvironment(new MockEnvironment().withProperty("bar.enabled", "true"));
104+
ctx.register(BeanConditionConfig.class);
105+
ctx.refresh();
106+
this.context = ctx;
107+
108+
FooService service = this.context.getBean(FooService.class);
109+
Cache cache = getCache();
110+
111+
Object key = new Object();
112+
Object value = service.getWithCondition(key);
113+
assertCacheHit(key, value, cache);
114+
value = service.getWithCondition(key);
115+
assertCacheHit(key, value, cache);
116+
117+
assertEquals(2, this.context.getBean(BeanConditionConfig.Bar.class).count);
88118
}
89119

90120
private Cache getCache() {
91121
return this.context.getBean(CacheManager.class).getCache("testCache");
92122
}
93123

124+
94125
@Configuration
95126
static class SharedConfig extends CachingConfigurerSupport {
127+
96128
@Override
97129
@Bean
98130
public CacheManager cacheManager() {
99131
return CacheTestUtils.createSimpleCacheManager("testCache");
100132
}
101133
}
102134

135+
103136
@Configuration
104137
@Import(SharedConfig.class)
105138
@EnableCaching
106139
static class FooConfig {
140+
107141
@Bean
108142
public FooService fooService() {
109143
return new FooServiceImpl();
110144
}
111145
}
112146

147+
113148
@Configuration
114149
@Import(SharedConfig.class)
115150
@EnableCaching(proxyTargetClass = true)
116151
static class FooConfigCglib {
152+
117153
@Bean
118154
public FooService fooService() {
119155
return new FooServiceImpl();
120156
}
121157
}
122158

159+
123160
private interface FooService {
161+
124162
Object getSimple(Object key);
125163

126164
Object getWithCondition(Object key);
127165
}
128166

167+
129168
@CacheConfig(cacheNames = "testCache")
130169
private static class FooServiceImpl implements FooService {
170+
131171
private final AtomicLong counter = new AtomicLong();
132172

133173
@Override
@@ -143,24 +183,33 @@ public Object getWithCondition(Object key) {
143183
}
144184
}
145185

186+
146187
@Configuration
147188
@Import(FooConfig.class)
148189
@EnableCaching
149190
static class BeanConditionConfig {
150191

192+
@Autowired
193+
Environment env;
194+
151195
@Bean
152196
public Bar bar() {
153-
return new Bar(false);
197+
return new Bar(Boolean.valueOf(env.getProperty("bar.enabled")));
154198
}
155199

200+
156201
static class Bar {
202+
203+
public int count;
204+
157205
private final boolean enabled;
158206

159207
public Bar(boolean enabled) {
160208
this.enabled = enabled;
161209
}
162210

163211
public boolean isEnabled() {
212+
this.count++;
164213
return this.enabled;
165214
}
166215
}

0 commit comments

Comments
 (0)