Polish "Evaluate key only if necessary"

See gh-22769
This commit is contained in:
Stephane Nicoll
2023-07-14 10:23:25 +02:00
parent 806c83591c
commit d8854a2f3f
8 changed files with 40 additions and 64 deletions

View File

@@ -119,10 +119,6 @@ public @interface CacheEvict {
* <p>The SpEL expression evaluates against a dedicated context that provides the * <p>The SpEL expression evaluates against a dedicated context that provides the
* following meta-data: * following meta-data:
* <ul> * <ul>
* <li>{@code #result} for a reference to the result of the method invocation, which
* can only be used if {@link #beforeInvocation()} is {@code false}. For supported
* wrappers such as {@code Optional}, {@code #result} refers to the actual object,
* not the wrapper</li>
* <li>{@code #root.method}, {@code #root.target}, and {@code #root.caches} for * <li>{@code #root.method}, {@code #root.target}, and {@code #root.caches} for
* references to the {@link java.lang.reflect.Method method}, target object, and * references to the {@link java.lang.reflect.Method method}, target object, and
* affected cache(s) respectively.</li> * affected cache(s) respectively.</li>

View File

@@ -131,9 +131,6 @@ public @interface Cacheable {
* <p>The SpEL expression evaluates against a dedicated context that provides the * <p>The SpEL expression evaluates against a dedicated context that provides the
* following meta-data: * following meta-data:
* <ul> * <ul>
* <li>{@code #result} for a reference to the result of the method invocation. For
* supported wrappers such as {@code Optional}, {@code #result} refers to the actual
* object, not the wrapper</li>
* <li>{@code #root.method}, {@code #root.target}, and {@code #root.caches} for * <li>{@code #root.method}, {@code #root.target}, and {@code #root.caches} for
* references to the {@link java.lang.reflect.Method method}, target object, and * references to the {@link java.lang.reflect.Method method}, target object, and
* affected cache(s) respectively.</li> * affected cache(s) respectively.</li>

View File

@@ -21,7 +21,6 @@ import java.lang.reflect.Proxy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@@ -417,7 +416,7 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
} }
// Collect puts from any @Cacheable miss, if no cached item is found // Collect puts from any @Cacheable miss, if no cached item is found
List<CachePutRequest> cachePutRequests = new LinkedList<>(); List<CachePutRequest> cachePutRequests = new ArrayList<>();
if (cacheHit == null) { if (cacheHit == null) {
collectPutRequests(contexts.get(CacheableOperation.class), cacheValue, cachePutRequests); collectPutRequests(contexts.get(CacheableOperation.class), cacheValue, cachePutRequests);
} }

View File

@@ -54,6 +54,7 @@
<cache:cache-put method="multiUpdate" cache="primary"/> <cache:cache-put method="multiUpdate" cache="primary"/>
<cache:cache-put method="multiUpdate" cache="secondary"/> <cache:cache-put method="multiUpdate" cache="secondary"/>
<cache:cache-put method="putRefersToResult" cache="primary" key="#result.id"/> <cache:cache-put method="putRefersToResult" cache="primary" key="#result.id"/>
<cache:cache-put method="putEvaluatesUnlessBeforeKey" cache="primary" key="#result.id" unless="#result == null"/>
</cache:caching> </cache:caching>
</cache:advice> </cache:advice>
@@ -93,6 +94,7 @@
<cache:cache-put method="multiUpdate" cache="primary"/> <cache:cache-put method="multiUpdate" cache="primary"/>
<cache:cache-put method="multiUpdate" cache="secondary"/> <cache:cache-put method="multiUpdate" cache="secondary"/>
<cache:cache-put method="putRefersToResult" cache="primary" key="#result.id"/> <cache:cache-put method="putRefersToResult" cache="primary" key="#result.id"/>
<cache:cache-put method="putEvaluatesUnlessBeforeKey" cache="primary" key="#result.id" unless="#result == null"/>
</cache:caching> </cache:caching>
</cache:advice> </cache:advice>

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -31,13 +31,10 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.testfixture.cache.beans.AnnotatedClassCacheableService; import org.springframework.context.testfixture.cache.beans.AnnotatedClassCacheableService;
import org.springframework.context.testfixture.cache.beans.CacheableService; import org.springframework.context.testfixture.cache.beans.CacheableService;
import org.springframework.context.testfixture.cache.beans.TestEntity; import org.springframework.context.testfixture.cache.beans.TestEntity;
import org.springframework.expression.spel.SpelEvaluationException;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIOException; import static org.assertj.core.api.Assertions.assertThatIOException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
/** /**
* Abstract cache annotation tests (containing several reusable methods). * Abstract cache annotation tests (containing several reusable methods).
@@ -507,27 +504,24 @@ public abstract class AbstractCacheAnnotationTests {
assertThat(primary.get(id).get()).isSameAs(entity); assertThat(primary.get(id).get()).isSameAs(entity);
} }
public void testPutRefersToNullResult(CacheableService<?> service) { protected void testPutRefersToResultWithUnless(CacheableService<?> service) {
Long id = Long.MIN_VALUE; Long id = 42L;
TestEntity entity = new TestEntity(); TestEntity entity = new TestEntity();
entity.setId(id);
Cache primary = this.cm.getCache("primary"); Cache primary = this.cm.getCache("primary");
assertNull(primary.get(id)); assertThat(primary.get(id)).isNull();
try { assertThat(service.putEvaluatesUnlessBeforeKey(entity)).isNotNull();
service.putRefersToNullResult(entity); assertThat(primary.get(id).get()).isSameAs(entity);
} catch (Exception e) {
assertEquals(SpelEvaluationException.class, e.getClass());
assertEquals("EL1007E: Property or field 'id' cannot be found on null", e.getMessage());
}
assertNull(primary.get(id));
} }
public void testPutRefersToNullResultWithUnless(CacheableService<?> service) { protected void testPutEvaluatesUnlessBeforeKey(CacheableService<?> service) {
Long id = Long.MIN_VALUE; Long id = Long.MIN_VALUE; // return null
TestEntity entity = new TestEntity(); TestEntity entity = new TestEntity();
entity.setId(id);
Cache primary = this.cm.getCache("primary"); Cache primary = this.cm.getCache("primary");
assertNull(primary.get(id)); assertThat(primary.get(id)).isNull();
service.putRefersToNullResultWithUnless(entity); assertThat(service.putEvaluatesUnlessBeforeKey(entity)).isNull();
assertNull(primary.get(id)); assertThat(primary.get(id)).isNull();
} }
protected void testMultiCacheAndEvict(CacheableService<?> service) { protected void testMultiCacheAndEvict(CacheableService<?> service) {
@@ -875,29 +869,29 @@ public abstract class AbstractCacheAnnotationTests {
testPutRefersToResult(this.cs); testPutRefersToResult(this.cs);
} }
@Test
public void testPutRefersToResultWithUnless() {
testPutRefersToResultWithUnless(this.cs);
}
@Test
public void testPutEvaluatesUnlessBeforeKey() {
testPutEvaluatesUnlessBeforeKey(this.cs);
}
@Test @Test
public void testClassPutRefersToResult() { public void testClassPutRefersToResult() {
testPutRefersToResult(this.ccs); testPutRefersToResult(this.ccs);
} }
@Test @Test
public void testPutRefersToNullResult() throws Exception { public void testClassPutRefersToResultWithUnless(){
testPutRefersToNullResult(this.cs); testPutRefersToResultWithUnless(this.ccs);
} }
@Test @Test
public void testClassPutRefersToNullResult() throws Exception { public void testClassPutEvaluatesUnlessBeforeKey(){
testPutRefersToNullResult(this.ccs); testPutEvaluatesUnlessBeforeKey(this.ccs);
}
@Test
public void testPutRefersToNullResultWithUnless() throws Exception {
testPutRefersToNullResultWithUnless(this.cs);
}
@Test
public void testClassPutRefersToNullResultWithUnless() throws Exception {
testPutRefersToNullResultWithUnless(this.ccs);
} }
@Test @Test

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -236,14 +236,9 @@ public class AnnotatedClassCacheableService implements CacheableService<Object>
} }
@Override @Override
@CachePut(cacheNames = "primary", key = "#result.id") @CachePut(cacheNames = "primary", key = "#result.id", unless = "#result == null")
public TestEntity putRefersToNullResult(TestEntity arg1) { public TestEntity putEvaluatesUnlessBeforeKey(TestEntity arg1) {
return null; return (arg1.getId() != Long.MIN_VALUE ? arg1 : null);
} }
@Override
@CachePut(cacheNames = "primary", key = "#result.id", unless = "#result == null")
public TestEntity putRefersToNullResultWithUnless(TestEntity arg1) {
return null;
}
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -93,8 +93,6 @@ public interface CacheableService<T> {
TestEntity putRefersToResult(TestEntity arg1); TestEntity putRefersToResult(TestEntity arg1);
TestEntity putRefersToNullResult(TestEntity arg1); TestEntity putEvaluatesUnlessBeforeKey(TestEntity arg1);
TestEntity putRefersToNullResultWithUnless(TestEntity arg1);
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -244,14 +244,9 @@ public class DefaultCacheableService implements CacheableService<Long> {
} }
@Override @Override
@CachePut(cacheNames = "primary", key = "#result.id") @CachePut(cacheNames = "primary", key = "#result.id", unless = "#result == null")
public TestEntity putRefersToNullResult(TestEntity arg1) { public TestEntity putEvaluatesUnlessBeforeKey(TestEntity arg1) {
return null; return (arg1.getId() != Long.MIN_VALUE ? arg1 : null);
} }
@Override
@CachePut(cacheNames = "primary", key = "#result.id", unless = "#result == null")
public TestEntity putRefersToNullResultWithUnless(TestEntity arg1) {
return null;
}
} }