美文网首页Java程序员SpringFramework
SpringBoot中使用Redis实现缓存

SpringBoot中使用Redis实现缓存

作者: Joepis | 来源:发表于2017-03-30 17:09 被阅读1710次

    摘要: 原创出处 http://peijie2016.gitee.io 欢迎转载,保留摘要,谢谢!

    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是包名+方法名+参数列表,这样就很难会冲突了。

    相关文章

      网友评论

        本文标题:SpringBoot中使用Redis实现缓存

        本文链接:https://www.haomeiwen.com/subject/brxpottx.html