配置

  1. 继承CachingConfigurerSupport,实现redis的缓存配置。
  • 配置类@Configuration
  • 开启缓存@EnableCaching
  • 从配置文件中读取redis配置项,注入bean@EnableConfigurationProperties(RedisProperties.class)

@EnableConfigurationProperties注解的作用是:使使用 @ConfigurationProperties 注解的类生效。

如果一个配置类只配置@ConfigurationProperties注解,而没有使用@Component,那么在IOC容器中是获取不到properties 配置文件转化的bean。说白了 @EnableConfigurationProperties 相当于把使用 @ConfigurationProperties 的类进行了一次注入。 作者:咪雅先森链接:https://www.jianshu.com/p/7f54da1cb2eb来源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  1. 全局配置redis:返回RedisCacheConfiguration对象
  • 配置RedisTemplate:设置序列化
  • 配置key生成器:返回KeyGenerator对象
  • 配置redis异常处理:返回CacheErrorHandler对象
  1. //redis配置类
  2. @Slf4j
  3. @Configuration
  4. @EnableCaching
  5. @ConditionalOnClass(RedisOperations.class)
  6. @EnableConfigurationProperties(RedisProperties.class)
  7. public class RedisConfig extends CachingConfigurerSupport {
  8. /**
  9. * 设置 redis 数据默认过期时间,默认2小时
  10. * 设置@cacheable 序列化方式
  11. */
  12. @Bean
  13. public RedisCacheConfiguration redisCacheConfiguration() {
  14. FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
  15. RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
  16. configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(2));
  17. return configuration;
  18. }
  19. @SuppressWarnings("all")
  20. @Bean(name = "redisTemplate")
  21. @ConditionalOnMissingBean(name = "redisTemplate")
  22. public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  23. RedisTemplate<Object, Object> template = new RedisTemplate<>();
  24. //序列化
  25. FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
  26. // value值的序列化采用fastJsonRedisSerializer
  27. template.setValueSerializer(fastJsonRedisSerializer);
  28. template.setHashValueSerializer(fastJsonRedisSerializer);
  29. // 全局开启AutoType,这里方便开发,使用全局的方式
  30. ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
  31. // 建议使用这种方式,小范围指定白名单
  32. // ParserConfig.getGlobalInstance().addAccept("me.zhengjie.domain");
  33. // key的序列化采用StringRedisSerializer
  34. template.setKeySerializer(new StringRedisSerializer());
  35. template.setHashKeySerializer(new StringRedisSerializer());
  36. template.setConnectionFactory(redisConnectionFactory);
  37. return template;
  38. }
  39. /**
  40. * 自定义缓存key生成策略,默认将使用该策略
  41. */
  42. @Bean
  43. @Override
  44. public KeyGenerator keyGenerator() {
  45. return (target, method, params) -> {
  46. Map<String, Object> container = new HashMap<>(3);
  47. Class<?> targetClassClass = target.getClass();
  48. // 类地址
  49. container.put("class", targetClassClass.toGenericString());
  50. // 方法名称
  51. container.put("methodName", method.getName());
  52. // 包名称
  53. container.put("package", targetClassClass.getPackage());
  54. // 参数列表
  55. for (int i = 0; i < params.length; i++) {
  56. container.put(String.valueOf(i), params[i]);
  57. }
  58. // 转为JSON字符串
  59. String jsonString = JSON.toJSONString(container);
  60. // 做SHA256 Hash计算,得到一个SHA256摘要作为Key
  61. return DigestUtils.sha256Hex(jsonString);
  62. };
  63. }
  64. @Bean
  65. @Override
  66. public CacheErrorHandler errorHandler() {
  67. // 异常处理,当Redis发生异常时,打印日志,但是程序正常走
  68. log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
  69. return new CacheErrorHandler() {
  70. @Override
  71. public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
  72. log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
  73. }
  74. @Override
  75. public void handleCachePutError(RuntimeException e, Cache cache, Object key, Object value) {
  76. log.error("Redis occur handleCachePutError:key -> [{}];value -> [{}]", key, value, e);
  77. }
  78. @Override
  79. public void handleCacheEvictError(RuntimeException e, Cache cache, Object key) {
  80. log.error("Redis occur handleCacheEvictError:key -> [{}]", key, e);
  81. }
  82. @Override
  83. public void handleCacheClearError(RuntimeException e, Cache cache) {
  84. log.error("Redis occur handleCacheClearError:", e);
  85. }
  86. };
  87. }
  88. }

key、value序列化用到的自定义类:


/**
 * Value 序列化
 *
 * @author /
 * @param <T>
 */
class FastJsonRedisSerializer<T> implements RedisSerializer<T> {

    private final Class<T> clazz;

    FastJsonRedisSerializer(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) {
        if (t == null) {
            return new byte[0];
        }
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8);
    }

    @Override
    public T deserialize(byte[] bytes) {
        if (bytes == null || bytes.length <= 0) {
            return null;
        }
        String str = new String(bytes, StandardCharsets.UTF_8);
        return JSON.parseObject(str, clazz);
    }

}

/**
 * 重写序列化器
 *
 * @author /
 */
class StringRedisSerializer implements RedisSerializer<Object> {

    private final Charset charset;

    StringRedisSerializer() {
        this(StandardCharsets.UTF_8);
    }

    private StringRedisSerializer(Charset charset) {
        Assert.notNull(charset, "Charset must not be null!");
        this.charset = charset;
    }

    @Override
    public String deserialize(byte[] bytes) {
        return (bytes == null ? null : new String(bytes, charset));
    }

    @Override
    public byte[] serialize(Object object) {
        String string = JSON.toJSONString(object);
        if (StringUtils.isBlank(string)) {
            return null;
        }
        string = string.replace("\"", "");
        return string.getBytes(charset);
    }
}

使用

添加缓存

在类方法上使用注解,需要指明cacheNames,这个是使用的cache名称,一个cache下可以放多个key-value。体现在key的开头就是你设置的cacheNames
注意:

  • 需要设置cacheNames
  • 序列化的对象需要实现Serializable接口(加上即可)

image.png
示例:


@Cacheable(cacheNames = "lujia")
public List<LocationItemDto> prepareCameraList(List<CameraInfo> cameraInfos) {
    ...
        ArrayList<LocationItemDto> res = new ArrayList<>(map.values());
    return res;
}


@Data
public class LocationItemDto implements Serializable {
    ....
}

redis:
image.png

删除缓存

@CacheEvict标注的方法可以在调用删除指定key或全部key。放在接口上最合适不过了。


    @GetMapping("/clearCache")
    @ApiOperation("清除缓存")
    @CacheEvict(cacheNames = "lujia",allEntries = true)
    public ResponseEntity clearCache() {
        return new ResponseEntity(HttpStatus.OK);
    }

@CacheEvict

@CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。@CacheEvict可以指定的属性有value、key、condition、allEntries和beforeInvocation。其中value、key和condition的语义与@Cacheable对应的属性类似。即value表示清除操作是发生在哪些Cache上的(对应Cache的名称);key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。下面我们来介绍一下新出现的两个属性allEntries和beforeInvocation。

1.3.1 allEntries属性

allEntries是boolean类型,表示是否需要清除缓存中的所有元素。默认为false,表示不需要。当指定了allEntries为true时,Spring Cache将忽略指定的key。有的时候我们需要Cache一下清除所有的元素,这比一个一个清除元素更有效率。 @CacheEvict(value=”users”, allEntries=true) public void delete(Integer id) { System.out.println(“delete user by id: “ + id); }

参考

Spring缓存注解@Cacheable、@CacheEvict、@CachePut使用