Spring Cache使用

一、基本使用

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);
       });
   } 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值