Spring Data Redis 为我们封装了 Redis 客户端的各种操作, 简化使用.
当 Redis 当做数据库或者消息队列来操作时, 我们一般使用 RedisTemplate 来操作
当 Redis 作为缓存使用时, 我们可以将它作为 Spring Cache 的实现, 直接通过注解使用
关于 RedisTemplate 的使用可参考: http://blog.didispace.com/springbootredis/
下面总结使用 Redis 作为缓存
引入依赖
SpringBoot 从 1.4 版本开始,
spring-boot-starter-redis
依赖改名了.
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- <version>1.5.2.RELEASE</version>
- </dependency>
配置文件
- spring:
- redis:
- host: 127.0.0.1
- port: 6379
- timeout: 0
- database: 0
- pool:
- max-active: 8
- max-wait: -1
- max-idle: 8
- min-idle: 0
这样, SpringBoot 将会自动配置 redis, 注入相关 bean, 我们就可以使用 @CacheConfig,@Cacheable,@CachePut,@CacheEvict 了.
使用 Cache 注解时的问题
缓存对象集合中, 缓存是以 key-value 形式保存的. 当不指定缓存的 key 时, SpringBoot 会使用 SimpleKeyGenerator 生成 key.
- public class SimpleKeyGenerator implements KeyGenerator {
- @Override
- public Object generate(Object target, Method method, Object... params) {
- return generateKey(params);
- }
- /**
- * Generate a key based on the specified parameters.
- */
- public static Object generateKey(Object... params) {
- if (params.length == 0) {
- return SimpleKey.EMPTY;
- }
- if (params.length == 1) {
- Object param = params[0];
- if (param != null && !param.getClass().isArray()) {
- return param;
- }
- }
- return new SimpleKey(params);
- }
- }
- public SimpleKey(Object... elements) {
- Assert.notNull(elements, "Elements must not be null");
- this.params = new Object[elements.length];
- System.arraycopy(elements, 0, this.params, 0, elements.length);
- this.hashCode = Arrays.deepHashCode(this.params);
- }
查看源码可以发现, 它是使用方法参数组合生成的一个 key. 此时有一个问题: 如果 2 个方法, 参数是一样的, 但执行逻辑不同, 那么将会导致执行第二个方法时命中第一个方法的缓存. 解决办法是在 @Cacheable 注解参数中指定 key, 或者自己实现一个 KeyGenerator, 在注解中指定 KeyGenerator. 但是如果这样的情况很多, 每一个都要指定 key,KeyGenerator 很麻烦.
Spring 同样提供了方案: 继承
CachingConfigurerSupport
并重写 keyGenerator()
下面贴出代码:
- @EnableCaching
- @Configuration
- public class RedisCacheConfig extends CachingConfigurerSupport {
- @Autowired
- private JedisConnectionFactory jedisConnectionFactory;
- @Bean
- public RedisTemplate redisTemplate() {
- StringRedisTemplate redisTemplate = new StringRedisTemplate(jedisConnectionFactory);
- Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
- ObjectMapper om = new ObjectMapper();
- om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
- om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
- jackson2JsonRedisSerializer.setObjectMapper(om);
- redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
- redisTemplate.afterPropertiesSet();
- return redisTemplate;
- }
- @Bean
- public CacheManager cacheManager() {
- String[] cacheNames = {"app_default", "users", "blogs", "goods", "configs", "info"};
- RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate(), Arrays.asList(cacheNames));
- redisCacheManager.setDefaultExpiration(86400);
- return redisCacheManager;
- }
- @Bean
- public Cache cache() {
- return cacheManager().getCache("app_default");
- }
- @Bean
- @Override
- public KeyGenerator keyGenerator() {
- return (target, method, objects) -> {
- StringBuilder sb = new StringBuilder();
- sb.append(target.getClass().getName());
- sb.append("::" + method.getName() + ":");
- for (Object obj : objects) {
- sb.append(obj.toString());
- }
- return sb.toString();
- };
- }
- }
此时, 缓存的 key 是包名 + 方法名 + 参数列表, 这样就很难会冲突了.
来源: https://juejin.im/post/5afbe1dd6fb9a07aa0483085