1
1
/*
2
- * Copyright 2002-2015 the original author or authors.
2
+ * Copyright 2002-2016 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
23
23
import java .util .LinkedList ;
24
24
import java .util .List ;
25
25
import java .util .Map ;
26
+ import java .util .Optional ;
26
27
import java .util .concurrent .Callable ;
27
28
import java .util .concurrent .ConcurrentHashMap ;
28
29
29
30
import org .apache .commons .logging .Log ;
30
31
import org .apache .commons .logging .LogFactory ;
31
32
32
33
import org .springframework .aop .framework .AopProxyUtils ;
34
+ import org .springframework .beans .factory .BeanFactory ;
35
+ import org .springframework .beans .factory .BeanFactoryAware ;
33
36
import org .springframework .beans .factory .InitializingBean ;
34
37
import org .springframework .beans .factory .NoSuchBeanDefinitionException ;
35
38
import org .springframework .beans .factory .NoUniqueBeanDefinitionException ;
36
39
import org .springframework .beans .factory .SmartInitializingSingleton ;
37
40
import org .springframework .beans .factory .annotation .BeanFactoryAnnotationUtils ;
38
41
import org .springframework .cache .Cache ;
39
42
import org .springframework .cache .CacheManager ;
40
- import org .springframework .cache .support .SimpleValueWrapper ;
41
43
import org .springframework .context .ApplicationContext ;
42
- import org .springframework .context .ApplicationContextAware ;
43
44
import org .springframework .context .expression .AnnotatedElementKey ;
44
45
import org .springframework .expression .EvaluationContext ;
46
+ import org .springframework .lang .UsesJava8 ;
45
47
import org .springframework .util .Assert ;
46
48
import org .springframework .util .ClassUtils ;
47
49
import org .springframework .util .CollectionUtils ;
77
79
* @since 3.1
78
80
*/
79
81
public abstract class CacheAspectSupport extends AbstractCacheInvoker
80
- implements InitializingBean , SmartInitializingSingleton , ApplicationContextAware {
82
+ implements BeanFactoryAware , InitializingBean , SmartInitializingSingleton {
83
+
84
+ private static Class <?> javaUtilOptionalClass = null ;
85
+
86
+ static {
87
+ try {
88
+ javaUtilOptionalClass =
89
+ ClassUtils .forName ("java.util.Optional" , CacheAspectSupport .class .getClassLoader ());
90
+ }
91
+ catch (ClassNotFoundException ex ) {
92
+ // Java 8 not available - Optional references simply not supported then.
93
+ }
94
+ }
81
95
82
96
protected final Log logger = LogFactory .getLog (getClass ());
83
97
84
- /**
85
- * Cache of CacheOperationMetadata, keyed by {@link CacheOperationCacheKey}.
86
- */
87
98
private final Map <CacheOperationCacheKey , CacheOperationMetadata > metadataCache =
88
99
new ConcurrentHashMap <CacheOperationCacheKey , CacheOperationMetadata >(1024 );
89
100
90
- private final ExpressionEvaluator evaluator = new ExpressionEvaluator ();
101
+ private final CacheOperationExpressionEvaluator evaluator = new CacheOperationExpressionEvaluator ();
91
102
92
103
private CacheOperationSource cacheOperationSource ;
93
104
94
105
private KeyGenerator keyGenerator = new SimpleKeyGenerator ();
95
106
96
107
private CacheResolver cacheResolver ;
97
108
98
- private ApplicationContext applicationContext ;
109
+ private BeanFactory beanFactory ;
99
110
100
111
private boolean initialized = false ;
101
112
@@ -164,12 +175,26 @@ public CacheResolver getCacheResolver() {
164
175
return this .cacheResolver ;
165
176
}
166
177
178
+ /**
179
+ * Set the containing {@link BeanFactory} for {@link CacheManager} and other
180
+ * service lookups.
181
+ * @since 4.3
182
+ */
167
183
@ Override
184
+ public void setBeanFactory (BeanFactory beanFactory ) {
185
+ this .beanFactory = beanFactory ;
186
+ }
187
+
188
+ /**
189
+ * @deprecated as of 4.3, in favor of {@link #setBeanFactory}
190
+ */
191
+ @ Deprecated
168
192
public void setApplicationContext (ApplicationContext applicationContext ) {
169
- this .applicationContext = applicationContext ;
193
+ this .beanFactory = applicationContext ;
170
194
}
171
195
172
196
197
+ @ Override
173
198
public void afterPropertiesSet () {
174
199
Assert .state (getCacheOperationSource () != null , "The 'cacheOperationSources' property is required: " +
175
200
"If there are no cacheable methods, then don't use a cache aspect." );
@@ -181,7 +206,7 @@ public void afterSingletonsInstantiated() {
181
206
if (getCacheResolver () == null ) {
182
207
// Lazily initialize cache resolver via default cache manager...
183
208
try {
184
- setCacheManager (this .applicationContext .getBean (CacheManager .class ));
209
+ setCacheManager (this .beanFactory .getBean (CacheManager .class ));
185
210
}
186
211
catch (NoUniqueBeanDefinitionException ex ) {
187
212
throw new IllegalStateException ("No CacheResolver specified, and no unique bean of type " +
@@ -282,7 +307,7 @@ else if (StringUtils.hasText(operation.getCacheManager())) {
282
307
* @see CacheOperation#cacheResolver
283
308
*/
284
309
protected <T > T getBean (String beanName , Class <T > expectedType ) {
285
- return BeanFactoryAnnotationUtils .qualifiedBeanOfType (this .applicationContext , expectedType , beanName );
310
+ return BeanFactoryAnnotationUtils .qualifiedBeanOfType (this .beanFactory , expectedType , beanName );
286
311
}
287
312
288
313
/**
@@ -294,13 +319,12 @@ protected void clearMetadataCache() {
294
319
}
295
320
296
321
protected Object execute (CacheOperationInvoker invoker , Object target , Method method , Object [] args ) {
297
- // check whether aspect is enabled
298
- // to cope with cases where the AJ is pulled in automatically
322
+ // Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically)
299
323
if (this .initialized ) {
300
324
Class <?> targetClass = getTargetClass (target );
301
325
Collection <CacheOperation > operations = getCacheOperationSource ().getCacheOperations (method , targetClass );
302
326
if (!CollectionUtils .isEmpty (operations )) {
303
- return execute (invoker , new CacheOperationContexts (operations , method , args , target , targetClass ));
327
+ return execute (invoker , method , new CacheOperationContexts (operations , method , args , target , targetClass ));
304
328
}
305
329
}
306
330
@@ -329,12 +353,12 @@ private Class<?> getTargetClass(Object target) {
329
353
return targetClass ;
330
354
}
331
355
332
- private Object execute (final CacheOperationInvoker invoker , CacheOperationContexts contexts ) {
356
+ private Object execute (final CacheOperationInvoker invoker , Method method , CacheOperationContexts contexts ) {
333
357
// Special handling of synchronized invocation
334
358
if (contexts .isSynchronized ()) {
335
359
CacheOperationContext context = contexts .get (CacheableOperation .class ).iterator ().next ();
336
- if (isConditionPassing (context , ExpressionEvaluator .NO_RESULT )) {
337
- Object key = generateKey (context , ExpressionEvaluator .NO_RESULT );
360
+ if (isConditionPassing (context , CacheOperationExpressionEvaluator .NO_RESULT )) {
361
+ Object key = generateKey (context , CacheOperationExpressionEvaluator .NO_RESULT );
338
362
Cache cache = context .getCaches ().iterator ().next ();
339
363
try {
340
364
return cache .get (key , new Callable <Object >() {
@@ -358,50 +382,65 @@ public Object call() throws Exception {
358
382
359
383
360
384
// Process any early evictions
361
- processCacheEvicts (contexts .get (CacheEvictOperation .class ), true , ExpressionEvaluator .NO_RESULT );
385
+ processCacheEvicts (contexts .get (CacheEvictOperation .class ), true ,
386
+ CacheOperationExpressionEvaluator .NO_RESULT );
362
387
363
388
// Check if we have a cached item matching the conditions
364
389
Cache .ValueWrapper cacheHit = findCachedItem (contexts .get (CacheableOperation .class ));
365
390
366
391
// Collect puts from any @Cacheable miss, if no cached item is found
367
392
List <CachePutRequest > cachePutRequests = new LinkedList <CachePutRequest >();
368
393
if (cacheHit == null ) {
369
- collectPutRequests (contexts .get (CacheableOperation .class ), ExpressionEvaluator .NO_RESULT , cachePutRequests );
394
+ collectPutRequests (contexts .get (CacheableOperation .class ),
395
+ CacheOperationExpressionEvaluator .NO_RESULT , cachePutRequests );
370
396
}
371
397
372
- Cache .ValueWrapper result = null ;
398
+ Object cacheValue ;
399
+ Object returnValue ;
373
400
374
- // If there are no put requests, just use the cache hit
375
- if (cachePutRequests .isEmpty () && !hasCachePut (contexts )) {
376
- result = cacheHit ;
401
+ if (cacheHit != null && cachePutRequests .isEmpty () && !hasCachePut (contexts )) {
402
+ // If there are no put requests, just use the cache hit
403
+ cacheValue = cacheHit .get ();
404
+ if (method .getReturnType () == javaUtilOptionalClass &&
405
+ (cacheValue == null || cacheValue .getClass () != javaUtilOptionalClass )) {
406
+ returnValue = OptionalUnwrapper .wrap (cacheValue );
407
+ }
408
+ else {
409
+ returnValue = cacheValue ;
410
+ }
377
411
}
378
-
379
- // Invoke the method if don't have a cache hit
380
- if (result == null ) {
381
- result = new SimpleValueWrapper (invokeOperation (invoker ));
412
+ else {
413
+ // Invoke the method if we don't have a cache hit
414
+ returnValue = invokeOperation (invoker );
415
+ if (returnValue != null && returnValue .getClass () == javaUtilOptionalClass ) {
416
+ cacheValue = OptionalUnwrapper .unwrap (returnValue );
417
+ }
418
+ else {
419
+ cacheValue = returnValue ;
420
+ }
382
421
}
383
422
384
423
// Collect any explicit @CachePuts
385
- collectPutRequests (contexts .get (CachePutOperation .class ), result . get () , cachePutRequests );
424
+ collectPutRequests (contexts .get (CachePutOperation .class ), cacheValue , cachePutRequests );
386
425
387
426
// Process any collected put requests, either from @CachePut or a @Cacheable miss
388
427
for (CachePutRequest cachePutRequest : cachePutRequests ) {
389
- cachePutRequest .apply (result . get () );
428
+ cachePutRequest .apply (cacheValue );
390
429
}
391
430
392
431
// Process any late evictions
393
- processCacheEvicts (contexts .get (CacheEvictOperation .class ), false , result . get () );
432
+ processCacheEvicts (contexts .get (CacheEvictOperation .class ), false , cacheValue );
394
433
395
- return result . get () ;
434
+ return returnValue ;
396
435
}
397
436
398
437
private boolean hasCachePut (CacheOperationContexts contexts ) {
399
- // Evaluate the conditions *without* the result object because we don't have it yet.
438
+ // Evaluate the conditions *without* the result object because we don't have it yet...
400
439
Collection <CacheOperationContext > cachePutContexts = contexts .get (CachePutOperation .class );
401
440
Collection <CacheOperationContext > excluded = new ArrayList <CacheOperationContext >();
402
441
for (CacheOperationContext context : cachePutContexts ) {
403
442
try {
404
- if (!context .isConditionPassing (ExpressionEvaluator .RESULT_UNAVAILABLE )) {
443
+ if (!context .isConditionPassing (CacheOperationExpressionEvaluator .RESULT_UNAVAILABLE )) {
405
444
excluded .add (context );
406
445
}
407
446
}
@@ -453,7 +492,7 @@ private void logInvalidating(CacheOperationContext context, CacheEvictOperation
453
492
* or {@code null} if none is found
454
493
*/
455
494
private Cache .ValueWrapper findCachedItem (Collection <CacheOperationContext > contexts ) {
456
- Object result = ExpressionEvaluator .NO_RESULT ;
495
+ Object result = CacheOperationExpressionEvaluator .NO_RESULT ;
457
496
for (CacheOperationContext context : contexts ) {
458
497
if (isConditionPassing (context , result )) {
459
498
Object key = generateKey (context , result );
@@ -551,7 +590,7 @@ public boolean isSynchronized() {
551
590
552
591
private boolean determineSyncFlag (Method method ) {
553
592
List <CacheOperationContext > cacheOperationContexts = this .contexts .get (CacheableOperation .class );
554
- if (cacheOperationContexts == null ) { // No @Cacheable operation
593
+ if (cacheOperationContexts == null ) { // no @Cacheable operation at all
555
594
return false ;
556
595
}
557
596
boolean syncEnabled = false ;
@@ -563,18 +602,18 @@ private boolean determineSyncFlag(Method method) {
563
602
}
564
603
if (syncEnabled ) {
565
604
if (this .contexts .size () > 1 ) {
566
- throw new IllegalStateException ("@Cacheable(sync = true) cannot be combined with other cache operations on '" + method + "'" );
605
+ throw new IllegalStateException ("@Cacheable(sync= true) cannot be combined with other cache operations on '" + method + "'" );
567
606
}
568
607
if (cacheOperationContexts .size () > 1 ) {
569
- throw new IllegalStateException ("Only one @Cacheable(sync = true) entry is allowed on '" + method + "'" );
608
+ throw new IllegalStateException ("Only one @Cacheable(sync= true) entry is allowed on '" + method + "'" );
570
609
}
571
610
CacheOperationContext cacheOperationContext = cacheOperationContexts .iterator ().next ();
572
611
CacheableOperation operation = (CacheableOperation ) cacheOperationContext .getOperation ();
573
612
if (cacheOperationContext .getCaches ().size () > 1 ) {
574
- throw new IllegalStateException ("@Cacheable(sync = true) only allows a single cache on '" + operation + "'" );
613
+ throw new IllegalStateException ("@Cacheable(sync= true) only allows a single cache on '" + operation + "'" );
575
614
}
576
615
if (StringUtils .hasText (operation .getUnless ())) {
577
- throw new IllegalStateException ("@Cacheable(sync = true) does not support unless attribute on '" + operation + "'" );
616
+ throw new IllegalStateException ("@Cacheable(sync= true) does not support unless attribute on '" + operation + "'" );
578
617
}
579
618
return true ;
580
619
}
@@ -702,9 +741,8 @@ protected Object generateKey(Object result) {
702
741
}
703
742
704
743
private EvaluationContext createEvaluationContext (Object result ) {
705
- return evaluator .createEvaluationContext (
706
- this .caches , this .metadata .method , this .args , this .target , this .metadata .targetClass ,
707
- result , applicationContext );
744
+ return evaluator .createEvaluationContext (this .caches , this .metadata .method , this .args ,
745
+ this .target , this .metadata .targetClass , result , beanFactory );
708
746
}
709
747
710
748
protected Collection <? extends Cache > getCaches () {
@@ -790,4 +828,26 @@ public int compareTo(CacheOperationCacheKey other) {
790
828
}
791
829
}
792
830
831
+
832
+ /**
833
+ * Inner class to avoid a hard dependency on Java 8.
834
+ */
835
+ @ UsesJava8
836
+ private static class OptionalUnwrapper {
837
+
838
+ public static Object unwrap (Object optionalObject ) {
839
+ Optional <?> optional = (Optional <?>) optionalObject ;
840
+ if (!optional .isPresent ()) {
841
+ return null ;
842
+ }
843
+ Object result = optional .get ();
844
+ Assert .isTrue (!(result instanceof Optional ), "Multi-level Optional usage not supported" );
845
+ return result ;
846
+ }
847
+
848
+ public static Object wrap (Object value ) {
849
+ return Optional .ofNullable (value );
850
+ }
851
+ }
852
+
793
853
}
0 commit comments