Support 'unless' expression for cache veto
Allow @Cachable, @CachePut and equivalent XML configuration to provide
a SpEL expression that can be used to veto putting an item into the
cache. Unlike 'condition' the 'unless' parameter is evaluated after
the method has been called and can therefore reference the #result.
For example:
@Cacheable(value="book",
condition="#name.length < 32",
unless="#result.hardback")
This commit also allows #result to be referenced from @CacheEvict
expressions as long as 'beforeInvocation' is false.
Issue: SPR-8871
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
* Copyright 2010-2013 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.cache.config;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -33,6 +35,7 @@ import org.springframework.context.ApplicationContext;
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public abstract class AbstractAnnotationTests {
|
||||
|
||||
@@ -187,6 +190,15 @@ public abstract class AbstractAnnotationTests {
|
||||
assertSame(r3, r4);
|
||||
}
|
||||
|
||||
public void testUnlessExpression(CacheableService<?> service) throws Exception {
|
||||
Cache cache = cm.getCache("default");
|
||||
cache.clear();
|
||||
service.unless(10);
|
||||
service.unless(11);
|
||||
assertThat(cache.get(10).get(), equalTo((Object) 10L));
|
||||
assertThat(cache.get(11), nullValue());
|
||||
}
|
||||
|
||||
public void testKeyExpression(CacheableService<?> service) throws Exception {
|
||||
Object r1 = service.key(5, 1);
|
||||
Object r2 = service.key(5, 2);
|
||||
@@ -441,6 +453,16 @@ public abstract class AbstractAnnotationTests {
|
||||
testConditionalExpression(cs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnlessExpression() throws Exception {
|
||||
testUnlessExpression(cs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassCacheUnlessExpression() throws Exception {
|
||||
testUnlessExpression(cs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyExpression() throws Exception {
|
||||
testKeyExpression(cs);
|
||||
@@ -618,4 +640,4 @@ public abstract class AbstractAnnotationTests {
|
||||
public void testClassMultiConditionalCacheAndEvict() {
|
||||
testMultiConditionalCacheAndEvict(ccs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -25,6 +25,7 @@ import org.springframework.cache.annotation.Caching;
|
||||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Cacheable("default")
|
||||
public class AnnotatedClassCacheableService implements CacheableService<Object> {
|
||||
@@ -42,6 +43,12 @@ public class AnnotatedClassCacheableService implements CacheableService<Object>
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "default", unless = "#result > 10")
|
||||
public Object unless(int arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict("default")
|
||||
public void invalidate(Object arg1) {
|
||||
@@ -157,4 +164,4 @@ public class AnnotatedClassCacheableService implements CacheableService<Object>
|
||||
public Object multiUpdate(Object arg1) {
|
||||
return arg1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -20,6 +20,7 @@ package org.springframework.cache.config;
|
||||
* Basic service interface.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public interface CacheableService<T> {
|
||||
|
||||
@@ -39,6 +40,8 @@ public interface CacheableService<T> {
|
||||
|
||||
T conditional(int field);
|
||||
|
||||
T unless(int arg);
|
||||
|
||||
T key(Object arg1, Object arg2);
|
||||
|
||||
T name(Object arg1);
|
||||
@@ -67,4 +70,4 @@ public interface CacheableService<T> {
|
||||
T multiConditionalCacheAndEvict(Object arg1);
|
||||
|
||||
T multiUpdate(Object arg1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -27,6 +27,7 @@ import org.springframework.cache.annotation.Caching;
|
||||
* Simple cacheable service
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DefaultCacheableService implements CacheableService<Long> {
|
||||
|
||||
@@ -78,6 +79,12 @@ public class DefaultCacheableService implements CacheableService<Long> {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "default", unless = "#result > 10")
|
||||
public Long unless(int arg) {
|
||||
return (long) arg;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "default", key = "#p0")
|
||||
public Long key(Object arg1, Object arg2) {
|
||||
@@ -163,4 +170,4 @@ public class DefaultCacheableService implements CacheableService<Long> {
|
||||
public Long multiUpdate(Object arg1) {
|
||||
return Long.valueOf(arg1.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -16,24 +16,39 @@
|
||||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.annotation.AnnotationCacheOperationSource;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import edu.emory.mathcs.backport.java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ExpressionEvalutatorTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private ExpressionEvaluator eval = new ExpressionEvaluator();
|
||||
|
||||
private AnnotationCacheOperationSource source = new AnnotationCacheOperationSource();
|
||||
@@ -59,6 +74,7 @@ public class ExpressionEvalutatorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMultipleCachingEval() throws Exception {
|
||||
AnnotatedClass target = new AnnotatedClass();
|
||||
Method method = ReflectionUtils.findMethod(AnnotatedClass.class, "multipleCaching", Object.class,
|
||||
@@ -78,9 +94,41 @@ public class ExpressionEvalutatorTest {
|
||||
assertEquals(args[1], keyB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withReturnValue() throws Exception {
|
||||
EvaluationContext context = createEvaluationContext("theResult");
|
||||
Object value = new SpelExpressionParser().parseExpression("#result").getValue(context);
|
||||
assertThat(value, equalTo((Object) "theResult"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withNullReturn() throws Exception {
|
||||
EvaluationContext context = createEvaluationContext(null);
|
||||
Object value = new SpelExpressionParser().parseExpression("#result").getValue(context);
|
||||
assertThat(value, nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withoutReturnValue() throws Exception {
|
||||
EvaluationContext context = createEvaluationContext(ExpressionEvaluator.NO_RESULT);
|
||||
Object value = new SpelExpressionParser().parseExpression("#result").getValue(context);
|
||||
assertThat(value, nullValue());
|
||||
}
|
||||
|
||||
private EvaluationContext createEvaluationContext(Object result) {
|
||||
AnnotatedClass target = new AnnotatedClass();
|
||||
Method method = ReflectionUtils.findMethod(AnnotatedClass.class, "multipleCaching", Object.class,
|
||||
Object.class);
|
||||
Object[] args = new Object[] { new Object(), new Object() };
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Cache> map = Collections.singleton(new ConcurrentMapCache("test"));
|
||||
EvaluationContext context = eval.createEvaluationContext(map, method, args, target, target.getClass(), result);
|
||||
return context;
|
||||
}
|
||||
|
||||
private static class AnnotatedClass {
|
||||
@Caching(cacheable = { @Cacheable(value = "test", key = "#a"), @Cacheable(value = "test", key = "#b") })
|
||||
public void multipleCaching(Object a, Object b) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user