一、基本使用
1、缓存配置类
@EnableCaching
@Configuration
public class CacheConfig {
@Primary // 多个CacheManager,需要指定其中一个为@Primary
@Bean
//public CustomCacheManager cacheManager(RedisTemplate redisTemplate) {
public RedisCacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.lockingRedisCacheWriter(redisTemplate.getConnectionFactory());
// redisTemplate 序列化使用的jdkSerializeable
JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer(this.getClass().getClassLoader());
RedisSerializationContext.SerializationPair objectSerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializationRedisSerializer);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
// 使用JDK序列化是二进制的,不要设置Key,否则redis中key的值看不懂
//.serializeKeysWith(objectSerializationPair)
.serializeValuesWith(objectSerializationPair)
.entryTtl(Duration.ofSeconds(60));
RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
return redisCacheManager;
// CustomCacheManager cacheManager = new CustomCacheManager(redisTemplate, redisCacheWriter, config);
// return cacheManager;
}
// JDK序列化,redis中key和value都是二进制
@Bean(name = "demoOneCacheManager")
public CustomCacheManager demoOneCacheManager(RedisTemplate redisTemplate) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.lockingRedisCacheWriter(redisTemplate.getConnectionFactory());
// redisTemplate 序列化使用的jdkSerializeable
JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer(this.getClass().getClassLoader());
RedisSerializationContext.SerializationPair objectSerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializationRedisSerializer);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
// 使用JDK序列化是二进制的,不要设置Key,否则redis中key的值看不懂
//.serializeKeysWith(objectSerializationPair)
.serializeValuesWith(objectSerializationPair)
.entryTtl(Duration.ofSeconds(1800));
CustomCacheManager cacheManager = new CustomCacheManager(redisTemplate, redisCacheWriter, redisCacheConfiguration);
//cacheManager.setChannelName("demo_one_channel");
return cacheManager;
}
// 自定义key序列化器,能够在redis中直观看到key和value的值
@Bean(name = "demoTwoCacheManager")
public CacheManager demoTwoCacheManager(RedisConnectionFactory redisConnectionFactory){
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
RedisSerializationContext.SerializationPair<String> keyObjectSerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(keySerializer());
RedisSerializationContext.SerializationPair<Object> valueObjectSerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer());
redisCacheConfiguration = redisCacheConfiguration
.serializeKeysWith(keyObjectSerializationPair)
.serializeValuesWith(valueObjectSerializationPair)
// 缓存超时时间10分钟
.entryTtl(Duration.ofMinutes(10L))
// 如果是空值,不缓存
.disableCachingNullValues();
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
RedisCacheManager build = RedisCacheManager.builder(redisCacheWriter).cacheDefaults(redisCacheConfiguration).build();
return build;
}
/**
* 自定义key序列化器
*/
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
/**
* 自定义value序列化器
*/
private RedisSerializer<Object> valueSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}
2、application配置文件
spring.redis.host=127.0.0.1
spring.redis.port=6379
3、实现类
Controller
@RestController
@RequestMapping("/demo/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/findUserById")
public Result findUserById(@RequestParam("uid") int userId, @RequestParam("sid") String sId){
User user = userService.findUserById(userId, sId);
return Result.success().data("results", user);
}
@PostMapping("/updateUser")
public Result updateUser(@RequestBody User user){
String sid = "12345";
User userResult = userService.updateUser(user, sid);
return Result.success().data("results", userResult);
}
@DeleteMapping("/deleteUserById")
public Result deleteUserById(@RequestParam("uid") int userId, @RequestParam("sid") String sId){
Integer userResult = userService.deleteUserById(userId, sId);
return Result.success().data("results", userResult);
}
service
public interface UserService extends IService<User> {
User findUserById(int userId, String sId);
User updateUser(User user, String sId);
Integer deleteUserById(int userId, String sId);
}
实现
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
// 新增缓存 redis中 key user:demo::userId:sId value 方法返回值
//@Cacheable(cacheNames = "user:demo", key = "#userId+':'+#sId", unless = "#result == null")
//@Cacheable(cacheManager = "demoOneCacheManager", cacheNames = "user:demo", key = "#userId+':'+#sId", unless = "#result == null")
@Cacheable(cacheManager = "demoTwoCacheManager", cacheNames = "user:demo", key = "#userId+':'+#sId", unless = "#result == null")
@Override
public User findUserById(int userId, String sId) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id", userId);
User user = baseMapper.selectOne(queryWrapper);
return user;
}
// 更新缓存
//@CachePut(cacheManager = "demoOneCacheManager", cacheNames = "user:demo", key = "#user.id+':'+#sId")
@CachePut(cacheManager = "demoTwoCacheManager", cacheNames = "user:demo", key = "#user.id+':'+#sId")
@Override
public User updateUser(User user, String sId) {
int i = baseMapper.updateById(user);
return user;
}
// 删除缓存
//@CacheEvict(cacheManager = "demoOneCacheManager", cacheNames = "user:demo", key = "#userId+':'+#sId")
@CacheEvict(cacheManager = "demoTwoCacheManager", cacheNames = "user:demo", key = "#userId+':'+#sId")
@Override
public Integer deleteUserById(int userId, String sId) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id", userId);
int delete = baseMapper.delete(queryWrapper);
return delete;
}
}
Mapper 接口
public interface UserMapper extends BaseMapper<User> {}
User
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="User对象", description="")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String name;
private String gender;
private String phone;
private Date createTime;
private Date updateTime;
}
至此可以实现 Spring Cache在项目中使用
二、多节点使用
结合本地缓存 caffeine 在分布式部署项目环境下使用
导入包
<!-- https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.8.5</version>
</dependency>
自定义Cache
@Slf4j
public class CustomCache implements Cache {
/**
* 本地缓存 caffeine
*/
private final com.github.benmanes.caffeine.cache.Cache<Object, Object> caffeineCacheMap;
private final RedisCache redisCache;
private final CustomCacheManager customCacheManager;
public CustomCache(CustomCacheManager customCacheManager, RedisCache redisCache) {
this.redisCache = redisCache;
this.customCacheManager = customCacheManager;
Duration expire = redisCache.getCacheConfiguration().getTtl();
// 初始化 caffeine 缓存
this.caffeineCacheMap = Caffeine.newBuilder().expireAfterWrite(expire).maximumSize(1000).build();
}
@Override
public String getName() {
return redisCache.getName();
}
@Override
public Object getNativeCache() {
return redisCache.getNativeCache();
}
@Override
public ValueWrapper get(@NotNull Object key) {
if (log.isInfoEnabled()) {
log.info("cacheName:{}, key:{}, localHashCode:{} ",this.getName(), key, caffeineCacheMap.hashCode());
}
ValueWrapper wrapper = (ValueWrapper)caffeineCacheMap.getIfPresent(key);
if (wrapper == null) {
wrapper = getFromRedis(key);
}
return wrapper;
}
/**
* 从 redis 获取缓存
* @param key
* @return
*/
private synchronized ValueWrapper getFromRedis(@NotNull Object key) {
ValueWrapper wrapper = (ValueWrapper)caffeineCacheMap.getIfPresent(key);
if (wrapper == null) {
if (log.isInfoEnabled()) {
log.info("getFromRedis cacheName:{}, key:{}, localHashCode:{} ", this.getName(), key, caffeineCacheMap.hashCode());
}
wrapper = redisCache.get(key);
if (wrapper != null) {
caffeineCacheMap.put(key, wrapper);
}
}
return wrapper;
}
@Override
public <T> T get(@NotNull Object key, Class<T> type) {
return redisCache.get(key, type);
}
@Override
public <T> T get(@NotNull Object key, @NotNull Callable<T> valueLoader) {
return redisCache.get(key, valueLoader);
}
@Override
public void put(@NotNull Object key, Object value) {
redisCache.put(key, value);
clearingOtherNodes();
}
@Override
public ValueWrapper putIfAbsent(@NotNull Object key, Object value) {
if (log.isInfoEnabled()) {
log.info("putIfAbsent cacheName:{}, key:{}, localHashCode:{}, value:{} ", this.getName(), key, caffeineCacheMap.hashCode(), value);
}
ValueWrapper v = redisCache.putIfAbsent(key, value);
clearingOtherNodes();
return v;
}
@Override
public void evict(@NotNull Object key) {
if (log.isInfoEnabled()) {
log.info("evict cacheName:{}, key:{}, localHashCode:{} ", this.getName(), key, caffeineCacheMap.hashCode());
}
redisCache.evict(key);
clearingOtherNodes();
}
@Override
public void clear() {
redisCache.clear();
}
public void clearLocal() {
this.caffeineCacheMap.invalidateAll();
}
/**
* 清除其它节点
*/
protected void clearingOtherNodes() {
customCacheManager.publishMessage(redisCache.getName());
}
}
自定义 RedisCacheManager
public class CustomCacheManager extends RedisCacheManager {
/**
* 缓存更新channel
*/
private static String CHANNEL_NAME = "demo_cache";
RedisTemplate redisTemplate;
public CustomCacheManager(RedisTemplate redisTemplate, RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
this.redisTemplate = redisTemplate;
}
/**
* 使用自定义 CustomCache 代替 Spring Boot自带的RedisCache
*/
@Override
protected Cache decorateCache(Cache cache) {
return new CustomCache(this, (RedisCache)cache);
}
/**
* redis发布信息,缓存同步其他分布式节点
*/
public void publishMessage(String cacheName) {
this.redisTemplate.convertAndSend(CHANNEL_NAME, cacheName);
}
/**
* 接受一个消息清空本地缓存
*/
public void receiver(String name) {
CustomCache cache = ((CustomCache)this.getCache(name));
if (cache != null) {
cache.clearLocal();
}
}
public String getChannelName() {
return CHANNEL_NAME;
}
public void setChannelName(String channelName) {
CHANNEL_NAME = channelName;
}
}
缓存配置类进行修改
@EnableCaching
@Configuration
public class CacheConfig {
@Primary // 多个CacheManager,需要指定其中一个为@Primary
@Bean
public CustomCacheManager cacheManager(RedisTemplate redisTemplate) {
//public RedisCacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.lockingRedisCacheWriter(redisTemplate.getConnectionFactory());
// redisTemplate 序列化使用的jdkSerializeable
JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer(this.getClass().getClassLoader());
RedisSerializationContext.SerializationPair objectSerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializationRedisSerializer);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
// 使用JDK序列化是二进制的,不要设置Key,否则redis中key的值看不懂
//.serializeKeysWith(objectSerializationPair)
.serializeValuesWith(objectSerializationPair)
.entryTtl(Duration.ofSeconds(60));
//RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
//return redisCacheManager;
CustomCacheManager cacheManager = new CustomCacheManager(redisTemplate, redisCacheWriter, redisCacheConfiguration);
return cacheManager;
}
// JDK序列化,redis中key和value都是二进制
@Bean(name = "demoOneCacheManager")
public CustomCacheManager demoOneCacheManager(RedisTemplate redisTemplate) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.lockingRedisCacheWriter(redisTemplate.getConnectionFactory());
// redisTemplate 序列化使用的jdkSerializeable
JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer(this.getClass().getClassLoader());
RedisSerializationContext.SerializationPair objectSerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializationRedisSerializer);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
// 使用JDK序列化是二进制的,不要设置Key,否则redis中key的值看不懂
//.serializeKeysWith(objectSerializationPair)
.serializeValuesWith(objectSerializationPair)
.entryTtl(Duration.ofSeconds(1800));
CustomCacheManager cacheManager = new CustomCacheManager(redisTemplate, redisCacheWriter, redisCacheConfiguration);
cacheManager.setChannelName("demo_one_channel");
return cacheManager;
}
// 自定义key序列化器,能够在redis中直观看到key和value的值
@Bean(name = "demoTwoCacheManager")
public CacheManager demoTwoCacheManager(RedisConnectionFactory redisConnectionFactory){
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
RedisSerializationContext.SerializationPair<String> keyObjectSerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(keySerializer());
RedisSerializationContext.SerializationPair<Object> valueObjectSerializationPair = RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer());
redisCacheConfiguration = redisCacheConfiguration
.serializeKeysWith(keyObjectSerializationPair)
.serializeValuesWith(valueObjectSerializationPair)
// 缓存超时时间10分钟
.entryTtl(Duration.ofMinutes(10L))
// 如果是空值,不缓存
.disableCachingNullValues();
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
RedisCacheManager build = RedisCacheManager.builder(redisCacheWriter).cacheDefaults(redisCacheConfiguration).build();
return build;
}
/**
* 自定义key序列化器
*/
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
/**
* 自定义value序列化器
*/
private RedisSerializer<Object> valueSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
/**
* redis 发布订阅
*/
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, final CustomCacheManager cacheManager,
@Autowired @Qualifier("demoOneCacheManager") final CustomCacheManager customCacheManager) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter(cacheManager), new PatternTopic(cacheManager.getChannelName()));
container.addMessageListener(listenerAdapter(customCacheManager), new PatternTopic(customCacheManager.getChannelName()));
return container;
}
private MessageListenerAdapter listenerAdapter(final CustomCacheManager cacheManager) {
return new MessageListenerAdapter((MessageListener) (message, pattern) -> {
// Sub 一个消息,通知缓存管理器
String cacheName = new String(message.getBody(), StandardCharsets.UTF_8);
cacheManager.receiver(cacheName);
});
}
}