-
在开发中如果是使用SpringCache来控制缓存的话,经常会遇到需要清除缓存的情况。我们希望其清除缓存时能支持模糊匹配,但是Spring自带的CacheManage是不支持模糊匹配的。
-
使用Redis为例。常用的缓存注解有:
@CachePut
@Cacheable
@CacheEvict
-
其中
@CacheEvict
是用于清除缓存的。但是其并不支持模糊匹配。这几个注解的功能是由类RedisCache
提供的。而清除缓存的功能是由evict
方法提供的。 -
@Override public void evict(Object key) { cacheWriter.remove(name, createAndConvertCacheKey(key)); }
-
很明显这个并不支持模糊匹配。
-
这时我们注意到了其中的
clear
方法 -
@Override public void clear() { byte[] pattern = conversionService.convert(createCacheKey("*"), byte[].class); cacheWriter.clean(name, pattern); }
-
clear
方法清除缓存的方法是通过模糊匹配清除的。这证明SpringCache是支持模糊匹配清除缓存的。而想调用clear
方法需要使用注解@CacheEvict(allEntries = true)
-
我们可以通过重写
evict
方法来使得清除缓存支持模糊匹配。
代码
重写RedisCache
/**
* 重写RedisCache使其支持模糊匹配
*/
private static final class CustomizedRedisCache extends RedisCache{
private final String name;
private final RedisCacheWriter cacheWriter;
private final ConversionService conversionService;
protected CustomizedRedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {
super(name, cacheWriter, cacheConfig);
this.name = name;
this.cacheWriter = cacheWriter;
this.conversionService = cacheConfig.getConversionService();
}
@Override
public void evict(Object key) {
byte[] pattern = this.conversionService.convert(this.createCacheKey(key), byte[].class);
this.cacheWriter.clean(this.name, pattern);
}
}
重写RedisCacheManager
-
由于
RedisCacheManage
中默认创建的是RedisCache
-
protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) { return new RedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig); }
-
所以我们需要重写
RedisCacheManager
来注入我们自己的RedisCache -
在创建CacheManager的时候记得换成我们自己定义的CacheManager
/**
* 重写RedisCacheManager,使得CustomizedRedisCache能够替换RedisCache
*/
private static final class CustomizedRedisCacheManager extends RedisCacheManager{
private final RedisCacheWriter cacheWriter;
private final RedisCacheConfiguration defaultCacheConfig;
private final Map<String, RedisCacheConfiguration> initialCaches = new LinkedHashMap<>();
private boolean enableTransactions;
public CustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, String... initialCacheNames) {
super(cacheWriter, defaultCacheConfiguration, initialCacheNames);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, boolean allowInFlightCacheCreation, String... initialCacheNames) {
super(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation, initialCacheNames);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation) {
super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
/**
* 这个构造方法最重要
**/
public CustomizedRedisCacheManager(RedisConnectionFactory redisConnectionFactory, RedisCacheConfiguration cacheConfiguration) {
this(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),cacheConfiguration);
}
//覆盖父类创建RedisCache
@Override
protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
return new CustomizedRedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
}
@Override
public Map<String, RedisCacheConfiguration> getCacheConfigurations() {
Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>(getCacheNames().size());
getCacheNames().forEach(it -> {
RedisCache cache = CustomizedRedisCache.class.cast(lookupCache(it));
configurationMap.put(it, cache != null ? cache.getCacheConfiguration() : null);
});
return Collections.unmodifiableMap(configurationMap);
}
}
完整代码
package cn.gloduck.onlinetest.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.lang.Nullable;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.*;
public class CacheConfig extends CachingConfigurerSupport {
@Autowired
private RedisConnectionFactory factory;
@Autowired
private CacheManager cacheManager;
/**
* 初始化redis
*
* @param redisConnectionFactory
* @return
*/
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer());
return redisTemplate;
}
@Bean
@Override
public CacheErrorHandler errorHandler() {
// 用于捕获从Cache中进行CRUD时的异常的回调处理器。
return new SimpleCacheErrorHandler();
}
@Bean
@Override
public CacheManager cacheManager() {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.
defaultCacheConfig()
.disableCachingNullValues()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()));
return new CustomizedRedisCacheManager(factory, cacheConfiguration);
}
@Bean
@Override
@DependsOn("cacheManager")
public CacheResolver cacheResolver() {
return new SimpleCacheResolver(cacheManager);
}
@Bean("customizedKeyGenerator")
public CustomizedKeyGenerator myKeyGenerator(){
return new CustomizedKeyGenerator();
}
/**
* Redis键序列化器
* @return
*/
private RedisSerializer keySerializer(){
return new StringRedisSerializer();
}
/**
* Redis值序列化器
* @return
*/
private RedisSerializer valueSerializer(){
return new Jackson2JsonRedisSerializer<Serializable>(Serializable.class);
}
/**
* 自定义Redis的KeyGenerator,取代自带的SimpleKeyGenerator
*/
private static final class CustomizedKeyGenerator implements KeyGenerator{
@Override
public Object generate(Object target, Method method, Object... params) {
if(params.length == 0){
return "defaultKey";
}
int length = params.length - 1;
StringBuilder keyBuilder = new StringBuilder();
for (int i = 0; i < length; i++) {
keyBuilder.append(params[i]).append("-");
}
keyBuilder.append(params[length]);
return keyBuilder.toString();
}
}
/**
* 重写RedisCache使其支持模糊匹配
*/
private static final class CustomizedRedisCache extends RedisCache{
private final String name;
private final RedisCacheWriter cacheWriter;
private final ConversionService conversionService;
protected CustomizedRedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig) {
super(name, cacheWriter, cacheConfig);
this.name = name;
this.cacheWriter = cacheWriter;
this.conversionService = cacheConfig.getConversionService();
}
@Override
public void evict(Object key) {
byte[] pattern = this.conversionService.convert(this.createCacheKey(key), byte[].class);
this.cacheWriter.clean(this.name, pattern);
}
}
/**
* 重写RedisCacheManager,使得CustomizedRedisCache能够替换RedisCache
*/
private static final class CustomizedRedisCacheManager extends RedisCacheManager{
private final RedisCacheWriter cacheWriter;
private final RedisCacheConfiguration defaultCacheConfig;
private final Map<String, RedisCacheConfiguration> initialCaches = new LinkedHashMap<>();
private boolean enableTransactions;
public CustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, String... initialCacheNames) {
super(cacheWriter, defaultCacheConfiguration, initialCacheNames);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, boolean allowInFlightCacheCreation, String... initialCacheNames) {
super(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation, initialCacheNames);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
public CustomizedRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation) {
super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation);
this.cacheWriter = cacheWriter;
this.defaultCacheConfig = defaultCacheConfiguration;
}
/**
* 这个构造方法最重要
**/
public CustomizedRedisCacheManager(RedisConnectionFactory redisConnectionFactory, RedisCacheConfiguration cacheConfiguration) {
this(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),cacheConfiguration);
}
//覆盖父类创建RedisCache
@Override
protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
return new CustomizedRedisCache(name, cacheWriter, cacheConfig != null ? cacheConfig : defaultCacheConfig);
}
@Override
public Map<String, RedisCacheConfiguration> getCacheConfigurations() {
Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>(getCacheNames().size());
getCacheNames().forEach(it -> {
RedisCache cache = CustomizedRedisCache.class.cast(lookupCache(it));
configurationMap.put(it, cache != null ? cache.getCacheConfiguration() : null);
});
return Collections.unmodifiableMap(configurationMap);
}
}
}
阅读量
loading...