22
33import cn .hutool .core .util .ObjectUtil ;
44import com .github .benmanes .caffeine .cache .Cache ;
5+ import lombok .Data ;
56import lombok .Getter ;
67import lombok .extern .slf4j .Slf4j ;
78import org .springframework .cache .support .AbstractValueAdaptingCache ;
@@ -58,8 +59,9 @@ public Object getNativeCache() {
5859 protected Object lookup (Object key ) {
5960 String redisKey = getRedisKey (key );
6061 Object val ;
61- // 去除 toStoreValue 的包装
62- val = fromStoreValue (caffeineCache .getIfPresent (key ));
62+ val = caffeineCache .getIfPresent (key );
63+ // val 是 toStoreValue 包装过的值,为 null 则 key 不存在
64+ // 因为存储的 null 值被包装成了 DLCacheNullVal.INSTANCE
6365 if (ObjectUtil .isNotNull (val )) {
6466 log .debug ("DLCache local get cache, key:{}, value:{}" , key , val );
6567 return val ;
@@ -101,11 +103,6 @@ public <T> T get(Object key, Callable<T> valueLoader) {
101103
102104 @ Override
103105 public void put (Object key , Object value ) {
104- // val 不能为空,但传了空,就清理该 key
105- if (!cacheProperties .isAllowNullValues () && ObjectUtil .isNull (value )) {
106- evict (key );
107- return ;
108- }
109106 putRemote (key , value );
110107 sendSyncMsg (key );
111108 putLocal (key , value );
@@ -119,15 +116,6 @@ public ValueWrapper putIfAbsent(Object key, Object value) {
119116 if (ObjectUtil .isNotNull (oldVal )) {
120117 return toValueWrapper (oldVal );
121118 }
122- // 若允许值为空,且 key 存在,但值为空
123- if (cacheProperties .isAllowNullValues () && ObjectUtil .equal (redisTemplate .hasKey (redisKey ), true )) {
124- return toValueWrapper (oldVal );
125- }
126- // val 不能为空,但传了空,就清理该 key
127- if (!cacheProperties .isAllowNullValues () && ObjectUtil .isNull (value )) {
128- evict (key );
129- return toValueWrapper (oldVal );
130- }
131119 Boolean setOkFlag ;
132120 if (expiration > 0 ) {
133121 setOkFlag = redisTemplate .opsForValue ().setIfAbsent (redisKey , value , expiration , TimeUnit .MILLISECONDS );
@@ -139,32 +127,28 @@ public ValueWrapper putIfAbsent(Object key, Object value) {
139127 putLocal (key , value );
140128 }
141129 return toValueWrapper (oldVal );
142-
143130 }
144131
145132 @ Override
146133 public void evict (Object key ) {
147134 // 先清理 redis 再清理 caffeine
148- redisTemplate . delete ( getRedisKey ( key ) );
135+ clearRemote ( key );
149136 sendSyncMsg (key );
150137 clearLocal (key );
151138 }
152139
153140 @ Override
154141 public void clear () {
155142 // 先清理 redis 再清理 caffeine
156- Set <String > keys = redisTemplate .keys (getRedisKey ("*" ));
157- if (ObjectUtil .isNotEmpty (keys )) {
158- keys .forEach (redisTemplate ::delete );
159- }
143+ clearRemote (null );
160144 sendSyncMsg (null );
161145 clearLocal (null );
162146 }
163147
164148 private void sendSyncMsg (Object key ) {
165149 String syncTopic = cacheProperties .getRemote ().getSyncTopic ();
166150 DLCacheRefreshMsg refreshMsg = new DLCacheRefreshMsg (name , key );
167- // 加入 SELF_MSG_MAP 防止重复处理
151+ // 加入 SELF_MSG_MAP 防止自身节点重复处理
168152 DLCacheRefreshListener .SELF_MSG_MAP .add (refreshMsg );
169153 redisTemplate .convertAndSend (syncTopic , refreshMsg );
170154 }
@@ -176,10 +160,63 @@ private void putLocal(Object key, Object value) {
176160
177161 private void putRemote (Object key , Object value ) {
178162 if (expiration > 0 ) {
179- redisTemplate .opsForValue ().set (getRedisKey (key ), value , expiration , TimeUnit .MILLISECONDS );
163+ // toStoreValue 包装 null 值
164+ redisTemplate .opsForValue ().set (getRedisKey (key ), toStoreValue (value ), expiration , TimeUnit .MILLISECONDS );
165+ return ;
166+ }
167+ redisTemplate .opsForValue ().set (getRedisKey (key ), toStoreValue (value ));
168+ }
169+
170+ public void clearRemote (Object key ) {
171+ if (ObjectUtil .isNull (key )) {
172+ Set <String > keys = redisTemplate .keys (getRedisKey ("*" ));
173+ if (ObjectUtil .isNotEmpty (keys )) {
174+ keys .forEach (redisTemplate ::delete );
175+ }
176+ return ;
177+ }
178+ redisTemplate .delete (getRedisKey (key ));
179+ }
180+
181+ public void clearLocal (Object key ) {
182+ if (ObjectUtil .isNull (key )) {
183+ caffeineCache .invalidateAll ();
180184 return ;
181185 }
182- redisTemplate .opsForValue ().set (getRedisKey (key ), value );
186+ caffeineCache .invalidate (key );
187+ }
188+
189+ /**
190+ * 检查是否允许缓存 null
191+ *
192+ * @param value 缓存值
193+ * @return 不为空则 true,为空但允许则 false,否则异常
194+ */
195+ private boolean checkValNotNull (Object value ) {
196+ if (ObjectUtil .isNotNull (value )) {
197+ return true ;
198+ }
199+ if (isAllowNullValues () && ObjectUtil .isNull (value )) {
200+ return false ;
201+ }
202+ // val 不能为空,但传了空
203+ throw new DLCacheException ("Check null val is not allowed" );
204+ }
205+
206+ @ Override
207+ protected Object fromStoreValue (Object storeValue ) {
208+ if (isAllowNullValues () && DLCacheNullVal .INSTANCE .equals (storeValue )) {
209+ return null ;
210+ }
211+ return storeValue ;
212+ }
213+
214+ @ Override
215+ protected Object toStoreValue (Object userValue ) {
216+ if (!checkValNotNull (userValue )) {
217+ return DLCacheNullVal .INSTANCE ;
218+ }
219+ return userValue ;
183220 }
184221
185222 /**
@@ -190,11 +227,12 @@ private String getRedisKey(Object key) {
190227 return this .name .concat ("::" ).concat (key .toString ());
191228 }
192229
193- public void clearLocal (Object key ) {
194- if (ObjectUtil .isNull (key )) {
195- caffeineCache .invalidateAll ();
196- } else {
197- caffeineCache .invalidate (key );
198- }
230+ /**
231+ * 在缓存时代替 null 值,以区分是 key 不存在还是 val 为 null
232+ */
233+ @ Data
234+ public static class DLCacheNullVal {
235+ public static final DLCacheNullVal INSTANCE = new DLCacheNullVal ();
236+ private String desc = "nullVal" ;
199237 }
200238}
0 commit comments