Skip to content

Commit 6b7662a

Browse files
author
lbw
committed
Merge branch 'feature/20220824--支持更换redisTemplate' of github.com:xiaomingfor/multilevel-cache-spring-boot-starter into hot
2 parents 487f45f + e6076fa commit 6b7662a

File tree

4 files changed

+79
-22
lines changed

4 files changed

+79
-22
lines changed

src/main/java/com/pig4cloud/plugin/cache/MultilevelCacheAutoConfiguration.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.pig4cloud.plugin.cache.properties.CacheConfigProperties;
44
import com.pig4cloud.plugin.cache.support.CacheMessageListener;
55
import com.pig4cloud.plugin.cache.support.RedisCaffeineCacheManager;
6+
import org.springframework.beans.factory.annotation.Qualifier;
67
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
78
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
89
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -28,10 +29,13 @@ public class MultilevelCacheAutoConfiguration {
2829
@Bean
2930
@ConditionalOnBean(RedisTemplate.class)
3031
public RedisCaffeineCacheManager cacheManager(CacheConfigProperties cacheConfigProperties,
31-
RedisTemplate<Object, Object> stringKeyRedisTemplate) {
32+
@Qualifier("stringKeyRedisTemplate") RedisTemplate<Object, Object> stringKeyRedisTemplate) {
3233
return new RedisCaffeineCacheManager(cacheConfigProperties, stringKeyRedisTemplate);
3334
}
3435

36+
/**
37+
* 可自定义名称为stringKeyRedisTemplate的RedisTemplate覆盖掉默认RedisTemplate。
38+
*/
3539
@Bean
3640
@ConditionalOnMissingBean(name = "stringKeyRedisTemplate")
3741
public RedisTemplate<Object, Object> stringKeyRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
@@ -44,11 +48,11 @@ public RedisTemplate<Object, Object> stringKeyRedisTemplate(RedisConnectionFacto
4448

4549
@Bean
4650
public RedisMessageListenerContainer cacheMessageListenerContainer(CacheConfigProperties cacheConfigProperties,
47-
RedisTemplate<Object, Object> stringKeyRedisTemplate, RedisCaffeineCacheManager redisCaffeineCacheManager) {
51+
@Qualifier("stringKeyRedisTemplate") RedisTemplate<Object, Object> stringKeyRedisTemplate,
52+
RedisCaffeineCacheManager redisCaffeineCacheManager) {
4853
RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
4954
redisMessageListenerContainer.setConnectionFactory(stringKeyRedisTemplate.getConnectionFactory());
50-
CacheMessageListener cacheMessageListener = new CacheMessageListener(stringKeyRedisTemplate,
51-
redisCaffeineCacheManager);
55+
CacheMessageListener cacheMessageListener = new CacheMessageListener(redisCaffeineCacheManager);
5256
redisMessageListenerContainer.addMessageListener(cacheMessageListener,
5357
new ChannelTopic(cacheConfigProperties.getRedis().getTopic()));
5458
return redisMessageListenerContainer;

src/main/java/com/pig4cloud/plugin/cache/support/CacheMessageListener.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import lombok.extern.slf4j.Slf4j;
55
import org.springframework.data.redis.connection.Message;
66
import org.springframework.data.redis.connection.MessageListener;
7-
import org.springframework.data.redis.core.RedisTemplate;
7+
import org.springframework.data.redis.serializer.RedisSerializer;
88

99
/**
1010
* @author fuwei.deng
@@ -14,13 +14,17 @@
1414
@RequiredArgsConstructor
1515
public class CacheMessageListener implements MessageListener {
1616

17-
private final RedisTemplate<Object, Object> redisTemplate;
17+
private RedisSerializer<Object> javaSerializer = RedisSerializer.java();
1818

1919
private final RedisCaffeineCacheManager redisCaffeineCacheManager;
2020

2121
@Override
2222
public void onMessage(Message message, byte[] pattern) {
23-
CacheMessage cacheMessage = (CacheMessage) redisTemplate.getValueSerializer().deserialize(message.getBody());
23+
24+
/**
25+
* 发送端固定了jdk序列户方式,接收端同样固定了jdk序列化方式进行反序列化。
26+
*/
27+
CacheMessage cacheMessage = (CacheMessage) javaSerializer.deserialize(message.getBody());
2428
log.debug("recevice a redis topic message, clear local cache, the cacheName is {}, the key is {}",
2529
cacheMessage.getCacheName(), cacheMessage.getKey());
2630
redisCaffeineCacheManager.clearLocal(cacheMessage.getCacheName(), cacheMessage.getKey());

src/main/java/com/pig4cloud/plugin/cache/support/RedisCaffeineCache.java

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
import lombok.Getter;
66
import lombok.extern.slf4j.Slf4j;
77
import org.springframework.cache.support.AbstractValueAdaptingCache;
8+
import org.springframework.cache.support.NullValue;
89
import org.springframework.data.redis.core.RedisTemplate;
9-
import org.springframework.data.redis.serializer.StringRedisSerializer;
10+
import org.springframework.data.redis.serializer.RedisSerializer;
11+
import org.springframework.util.Assert;
1012
import org.springframework.util.CollectionUtils;
1113
import org.springframework.util.StringUtils;
1214

@@ -44,6 +46,10 @@ public class RedisCaffeineCache extends AbstractValueAdaptingCache {
4446

4547
private final Map<String, ReentrantLock> keyLockMap = new ConcurrentHashMap<>();
4648

49+
private RedisSerializer<String> stringSerializer = RedisSerializer.string();
50+
51+
private RedisSerializer<Object> javaSerializer = RedisSerializer.java();
52+
4753
public RedisCaffeineCache(String name, RedisTemplate<Object, Object> stringKeyRedisTemplate,
4854
Cache<Object, Object> caffeineCache, CacheConfigProperties cacheConfigProperties) {
4955
super(cacheConfigProperties.isCacheNullValues());
@@ -105,11 +111,10 @@ public void put(Object key, Object value) {
105111

106112
@Override
107113
public ValueWrapper putIfAbsent(Object key, Object value) {
108-
Object cacheKey = getKey(key);
109114
Object prevValue;
110115
// 考虑使用分布式锁,或者将redis的setIfAbsent改为原子性操作
111116
synchronized (key) {
112-
prevValue = stringKeyRedisTemplate.opsForValue().get(cacheKey);
117+
prevValue = getRedisValue(key);
113118
if (prevValue == null) {
114119
doPut(key, value);
115120
}
@@ -118,14 +123,9 @@ public ValueWrapper putIfAbsent(Object key, Object value) {
118123
}
119124

120125
private void doPut(Object key, Object value) {
121-
Duration expire = getExpire(value);
122126
value = toStoreValue(value);
123-
if (!expire.isNegative() && !expire.isZero()) {
124-
stringKeyRedisTemplate.opsForValue().set(getKey(key), value, expire);
125-
}
126-
else {
127-
stringKeyRedisTemplate.opsForValue().set(getKey(key), value);
128-
}
127+
Duration expire = getExpire(value);
128+
setRedisValue(key, value, expire);
129129

130130
push(new CacheMessage(this.name, key));
131131

@@ -165,9 +165,7 @@ protected Object lookup(Object key) {
165165
return value;
166166
}
167167

168-
// 避免自动一个 RedisTemplate 覆盖失效
169-
stringKeyRedisTemplate.setKeySerializer(new StringRedisSerializer());
170-
value = stringKeyRedisTemplate.opsForValue().get(cacheKey);
168+
value = getRedisValue(key);
171169

172170
if (value != null) {
173171
log.debug("get cache from redis and put in caffeine, the key is : {}", cacheKey);
@@ -186,7 +184,7 @@ private Duration getExpire(Object value) {
186184
if (cacheNameExpire == null) {
187185
cacheNameExpire = defaultExpiration;
188186
}
189-
if (value == null && this.defaultNullValuesExpiration != null) {
187+
if ((value == null || value == NullValue.INSTANCE) && this.defaultNullValuesExpiration != null) {
190188
cacheNameExpire = this.defaultNullValuesExpiration;
191189
}
192190
return cacheNameExpire;
@@ -200,7 +198,19 @@ private Duration getExpire(Object value) {
200198
* @version 1.0.0
201199
*/
202200
private void push(CacheMessage message) {
203-
stringKeyRedisTemplate.convertAndSend(topic, message);
201+
202+
/**
203+
* 为了能自定义redisTemplate,发布订阅的序列化方式固定为jdk序列化方式。
204+
*/
205+
Assert.hasText(topic, "a non-empty channel is required");
206+
byte[] rawChannel = stringSerializer.serialize(topic);
207+
byte[] rawMessage = javaSerializer.serialize(message);
208+
stringKeyRedisTemplate.execute((connection) -> {
209+
connection.publish(rawChannel, rawMessage);
210+
return null;
211+
}, true);
212+
213+
// stringKeyRedisTemplate.convertAndSend(topic, message);
204214
}
205215

206216
/**
@@ -220,4 +230,29 @@ public void clearLocal(Object key) {
220230
}
221231
}
222232

233+
private void setRedisValue(Object key, Object value, Duration expire) {
234+
235+
Object convertValue = value;
236+
if (value == null || value == NullValue.INSTANCE) {
237+
convertValue = RedisNullValue.REDISNULLVALUE;
238+
}
239+
240+
if (!expire.isNegative() && !expire.isZero()) {
241+
stringKeyRedisTemplate.opsForValue().set(getKey(key), convertValue, expire);
242+
}
243+
else {
244+
stringKeyRedisTemplate.opsForValue().set(getKey(key), convertValue);
245+
}
246+
}
247+
248+
private Object getRedisValue(Object key) {
249+
250+
// NullValue在不同序列化方式中存在问题,因此自定义了RedisNullValue做个转化。
251+
Object value = stringKeyRedisTemplate.opsForValue().get(getKey(key));
252+
if (value != null && value instanceof RedisNullValue) {
253+
value = NullValue.INSTANCE;
254+
}
255+
return value;
256+
}
257+
223258
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.pig4cloud.plugin.cache.support;
2+
3+
import lombok.Data;
4+
5+
import java.io.Serializable;
6+
7+
@Data
8+
class RedisNullValue implements Serializable {
9+
10+
private static final long serialVersionUID = 1L;
11+
12+
public static RedisNullValue REDISNULLVALUE = new RedisNullValue();
13+
14+
}

0 commit comments

Comments
 (0)