24
24
import java .util .Map .Entry ;
25
25
import java .util .StringJoiner ;
26
26
import java .util .concurrent .Callable ;
27
+ import java .util .concurrent .locks .Lock ;
28
+ import java .util .concurrent .locks .ReentrantLock ;
27
29
28
30
import org .springframework .cache .Cache ;
29
31
import org .springframework .cache .support .AbstractValueAdaptingCache ;
@@ -58,6 +60,8 @@ public class RedisCache extends AbstractValueAdaptingCache {
58
60
59
61
private static final byte [] BINARY_NULL_VALUE = RedisSerializer .java ().serialize (NullValue .INSTANCE );
60
62
63
+ private final Lock lock = new ReentrantLock ();
64
+
61
65
private final RedisCacheConfiguration cacheConfiguration ;
62
66
63
67
private final RedisCacheWriter cacheWriter ;
@@ -68,12 +72,12 @@ public class RedisCache extends AbstractValueAdaptingCache {
68
72
* Create a new {@link RedisCache}.
69
73
*
70
74
* @param name {@link String name} for this {@link Cache}; must not be {@literal null}.
71
- * @param cacheWriter {@link RedisCacheWriter} used to perform {@link RedisCache} operations by executing
72
- * the necessary Redis commands; must not be {@literal null}.
73
- * @param cacheConfiguration {@link RedisCacheConfiguration} applied to this {@link RedisCache} on creation;
74
- * must not be {@literal null}.
75
+ * @param cacheWriter {@link RedisCacheWriter} used to perform {@link RedisCache} operations by executing the
76
+ * necessary Redis commands; must not be {@literal null}.
77
+ * @param cacheConfiguration {@link RedisCacheConfiguration} applied to this {@link RedisCache} on creation; must not
78
+ * be {@literal null}.
75
79
* @throws IllegalArgumentException if either the given {@link RedisCacheWriter} or {@link RedisCacheConfiguration}
76
- * are {@literal null} or the given {@link String} name for this {@link RedisCache} is {@literal null}.
80
+ * are {@literal null} or the given {@link String} name for this {@link RedisCache} is {@literal null}.
77
81
*/
78
82
protected RedisCache (String name , RedisCacheWriter cacheWriter , RedisCacheConfiguration cacheConfiguration ) {
79
83
@@ -92,7 +96,7 @@ protected RedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfig
92
96
* Get the {@link RedisCacheConfiguration} used to configure this {@link RedisCache} on initialization.
93
97
*
94
98
* @return an immutable {@link RedisCacheConfiguration} used to configure this {@link RedisCache} on initialization;
95
- * never {@literal null}.
99
+ * never {@literal null}.
96
100
*/
97
101
public RedisCacheConfiguration getCacheConfiguration () {
98
102
return this .cacheConfiguration ;
@@ -108,11 +112,11 @@ protected RedisCacheWriter getCacheWriter() {
108
112
}
109
113
110
114
/**
111
- * Gets the configured {@link ConversionService} used to convert {@link Object cache keys} to a {@link String}
112
- * when accessing entries in the cache.
115
+ * Gets the configured {@link ConversionService} used to convert {@link Object cache keys} to a {@link String} when
116
+ * accessing entries in the cache.
113
117
*
114
- * @return the configured {@link ConversionService} used to convert {@link Object cache keys} to a {@link String}
115
- * when accessing entries in the cache.
118
+ * @return the configured {@link ConversionService} used to convert {@link Object cache keys} to a {@link String} when
119
+ * accessing entries in the cache.
116
120
* @see RedisCacheConfiguration#getConversionService()
117
121
* @see #getCacheConfiguration()
118
122
*/
@@ -153,21 +157,27 @@ public <T> T get(Object key, Callable<T> valueLoader) {
153
157
154
158
@ Nullable
155
159
@ SuppressWarnings ("unchecked" )
156
- private synchronized <T > T getSynchronized (Object key , Callable <T > valueLoader ) {
160
+ private <T > T getSynchronized (Object key , Callable <T > valueLoader ) {
157
161
158
- ValueWrapper result = get (key );
162
+ lock .lock ();
163
+ try {
164
+ ValueWrapper result = get (key );
165
+
166
+ return result != null ? (T ) result .get () : loadCacheValue (key , valueLoader );
167
+ } finally {
168
+ lock .unlock ();
169
+
170
+ }
159
171
160
- return result != null ? (T ) result .get () : loadCacheValue (key , valueLoader );
161
172
}
162
173
163
174
/**
164
- * Loads the {@link Object} using the given {@link Callable valueLoader} and {@link #put(Object, Object) puts}
165
- * the {@link Object loaded value} in the cache.
175
+ * Loads the {@link Object} using the given {@link Callable valueLoader} and {@link #put(Object, Object) puts} the
176
+ * {@link Object loaded value} in the cache.
166
177
*
167
178
* @param <T> {@link Class type} of the loaded {@link Object cache value}.
168
179
* @param key {@link Object key} mapped to the loaded {@link Object cache value}.
169
- * @param valueLoader {@link Callable} object used to load the {@link Object value}
170
- * for the given {@link Object key}.
180
+ * @param valueLoader {@link Callable} object used to load the {@link Object value} for the given {@link Object key}.
171
181
* @return the loaded {@link Object value}.
172
182
*/
173
183
protected <T > T loadCacheValue (Object key , Callable <T > valueLoader ) {
@@ -176,8 +186,7 @@ protected <T> T loadCacheValue(Object key, Callable<T> valueLoader) {
176
186
177
187
try {
178
188
value = valueLoader .call ();
179
- }
180
- catch (Exception cause ) {
189
+ } catch (Exception cause ) {
181
190
throw new ValueRetrievalException (key , valueLoader , cause );
182
191
}
183
192
@@ -190,8 +199,8 @@ protected <T> T loadCacheValue(Object key, Callable<T> valueLoader) {
190
199
protected Object lookup (Object key ) {
191
200
192
201
byte [] value = getCacheConfiguration ().isTimeToIdleEnabled ()
193
- ? getCacheWriter ().get (getName (), createAndConvertCacheKey (key ), getTimeToLive (key ))
194
- : getCacheWriter ().get (getName (), createAndConvertCacheKey (key ));
202
+ ? getCacheWriter ().get (getName (), createAndConvertCacheKey (key ), getTimeToLive (key ))
203
+ : getCacheWriter ().get (getName (), createAndConvertCacheKey (key ));
195
204
196
205
return value != null ? deserializeCacheValue (value ) : null ;
197
206
}
@@ -212,14 +221,14 @@ public void put(Object key, @Nullable Object value) {
212
221
if (nullCacheValueIsNotAllowed (cacheValue )) {
213
222
214
223
String message = String .format ("Cache '%s' does not allow 'null' values; Avoid storing null"
215
- + " via '@Cacheable(unless=\" #result == null\" )' or configure RedisCache to allow 'null'"
216
- + " via RedisCacheConfiguration" , getName ());
224
+ + " via '@Cacheable(unless=\" #result == null\" )' or configure RedisCache to allow 'null'"
225
+ + " via RedisCacheConfiguration" , getName ());
217
226
218
227
throw new IllegalArgumentException (message );
219
228
}
220
229
221
230
getCacheWriter ().put (getName (), createAndConvertCacheKey (key ), serializeCacheValue (cacheValue ),
222
- getTimeToLive (key , value ));
231
+ getTimeToLive (key , value ));
223
232
}
224
233
225
234
@ Override
@@ -232,7 +241,7 @@ public ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
232
241
}
233
242
234
243
byte [] result = getCacheWriter ().putIfAbsent (getName (), createAndConvertCacheKey (key ),
235
- serializeCacheValue (cacheValue ), getTimeToLive (key , value ));
244
+ serializeCacheValue (cacheValue ), getTimeToLive (key , value ));
236
245
237
246
return result != null ? new SimpleValueWrapper (fromStoreValue (deserializeCacheValue (result ))) : null ;
238
247
}
@@ -313,7 +322,7 @@ protected byte[] serializeCacheValue(Object value) {
313
322
*
314
323
* @param value array of bytes to deserialize; must not be {@literal null}.
315
324
* @return an {@link Object} deserialized from the array of bytes using the configured value
316
- * {@link RedisSerializationContext.SerializationPair}; can be {@literal null}.
325
+ * {@link RedisSerializationContext.SerializationPair}; can be {@literal null}.
317
326
* @see RedisCacheConfiguration#getValueSerializationPair()
318
327
*/
319
328
@ Nullable
@@ -359,8 +368,7 @@ protected String convertKey(Object key) {
359
368
if (conversionService .canConvert (source , TypeDescriptor .valueOf (String .class ))) {
360
369
try {
361
370
return conversionService .convert (key , String .class );
362
- }
363
- catch (ConversionFailedException cause ) {
371
+ } catch (ConversionFailedException cause ) {
364
372
365
373
// May fail if the given key is a collection
366
374
if (isCollectionLikeOrMap (source )) {
@@ -375,8 +383,9 @@ protected String convertKey(Object key) {
375
383
return key .toString ();
376
384
}
377
385
378
- String message = String .format ("Cannot convert cache key %s to String; Please register a suitable Converter"
379
- + " via 'RedisCacheConfiguration.configureKeyConverters(...)' or override '%s.toString()'" ,
386
+ String message = String .format (
387
+ "Cannot convert cache key %s to String; Please register a suitable Converter"
388
+ + " via 'RedisCacheConfiguration.configureKeyConverters(...)' or override '%s.toString()'" ,
380
389
source , key .getClass ().getName ());
381
390
382
391
throw new IllegalStateException (message );
@@ -413,13 +422,12 @@ private String convertCollectionLikeOrMapKey(Object key, TypeDescriptor source)
413
422
target .append ("}" );
414
423
415
424
return target .toString ();
416
- }
417
- else if (source .isCollection () || source .isArray ()) {
425
+ } else if (source .isCollection () || source .isArray ()) {
418
426
419
427
StringJoiner stringJoiner = new StringJoiner ("," );
420
428
421
429
Collection <?> collection = source .isCollection () ? (Collection <?>) key
422
- : Arrays .asList (ObjectUtils .toObjectArray (key ));
430
+ : Arrays .asList (ObjectUtils .toObjectArray (key ));
423
431
424
432
for (Object collectedKey : collection ) {
425
433
stringJoiner .add (convertKey (collectedKey ));
0 commit comments