美文网首页javaWeb学习
SpringBoot2与RedisCacheManager整合

SpringBoot2与RedisCacheManager整合

作者: 帅大叔的简书 | 来源:发表于2019-08-08 23:06 被阅读0次

    SpringBoot 缓存管理器CacheManager

    从3.1开始Spring定义了org.springframework.cache.Cacheorg.springframework.cache.CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107) 注解简化开发.

    • Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
    • Cache接口下Spring提供了各种xxxCache的实现;如RedisCacheEhCacheCache ,ConcurrentMapCache等;

    快速开始

    1、导入Maven

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    
    <!-- redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    

    因为需要用RedisCacheManager 所以导入Redis依赖

    2、配置Redis并开启缓存支持

    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.CachingConfigurerSupport;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheConfiguration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializationContext;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    import javax.annotation.Resource;
    import java.time.Duration;
    
    /**
     * 
     * @author rstyro
     * @time 2018-07-31
     *
     */
    @Configuration
    @EnableCaching // 开启缓存支持
    public class RedisConfig extends CachingConfigurerSupport {
        @Resource
        private LettuceConnectionFactory lettuceConnectionFactory;
    
    
        /**
         * 配置CacheManager
         * @return
         */
        @Bean
        public CacheManager cacheManager() {
            RedisSerializer<String> redisSerializer = new StringRedisSerializer();
            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);
    
            // 配置序列化(解决乱码的问题)
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
    //                .entryTtl(Duration.ZERO)
                    .entryTtl(Duration.ofSeconds(20))   //设置缓存失效时间
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                    .disableCachingNullValues();
    
            RedisCacheManager cacheManager = RedisCacheManager.builder(lettuceConnectionFactory)
                    .cacheDefaults(config)
                    .build();
            return cacheManager;
        }
    
    
        /**
         * RedisTemplate配置
         */
        @Bean
        public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
            // 设置序列化
            Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(
                    Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            // 配置redisTemplate
            RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
            redisTemplate.setConnectionFactory(lettuceConnectionFactory);
            RedisSerializer<?> stringSerializer = new StringRedisSerializer();
            redisTemplate.setKeySerializer(stringSerializer);// key序列化
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化
            redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化
            redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }
    
    
    }
    
    

    3、注解解析

    注解的几个属性说明:

    //指定缓存组件的名字
    @AliasFor("cacheNames")
    String[] value() default {};
    
    //指定缓存组件的名字
    @AliasFor("value")
    String[] cacheNames() default {};
    
    // 缓存数据使用的Key,
    String key() default "";
    
    // key的生成器,可以自己指定key的生成组件id; 
    // key 与 keyGenerator 两个只能选一个,不能同时指定
    String keyGenerator() default "";
    
    // 指定缓存管理器,
    String cacheManager() default "";
    
    //指定获得解析器,cacheManager、cacheResolver 两者取其一
    String cacheResolver() default "";
    
    // 符合condition条件,才缓存
    String condition() default "";
    
    // 和 codition 条件相反才成立
    String unless() default "";
    
    // 是否使用异步模式
    boolean sync() default false;
    
    // 只有@CacheEvict 有这个属性
    // 清空所有缓存信息
    boolean allEntries() default false;
    
    // 只有@CacheEvict 有这个属性
    // 缓存的清除是否在方法之前执行
    // beforeInvocation=false  默认是在方法之后执行
    boolean beforeInvocation() default false;
    
    • @Cacheable
      先查询缓存中是否存在,存在则返回缓存内容,反正执行方法后返回并把返回结果缓存起来
    • @CacheEvict
      删除指定缓存
    • @CachePut
      更新并刷新缓存,先执行方法内容,然后更新缓存
    • @EnableCaching
      这个是一个复合注解,可以拥有同时配置上面3个注解的功能

    4、使用方法

    @CacheConfig(cacheNames = "act")  //这个注解表示类中共同放入到act 模块中
    @Service
    public class ActicleService implements IActicleService {
    
        @Autowired
        private ActicleMapper acticleMapper;
    
        /**
         * @Cacheable
         * 1、先查缓存,
         * 2、若没有缓存,就执行方法
         * 3、若有缓存。则返回,不执行方法
         *
         * 所以@Cacheable 不能使用result
         *
         * @return
         * @throws Exception
         */
        @Cacheable(key = "#root.methodName")
        public List<Acticle> list() throws Exception {
            return acticleMapper.getActicleList();
        }
    
        /**
         * @CachePut 更新并刷新缓存
         * 1、先调用目标方法
         * 2、把结果缓存
         * @param acticle
         * @return
         * @throws Exception
         */
        @CachePut(key = "#result.id" ,unless = "#result.id == null" )
        public Acticle save(Acticle acticle) throws Exception {
            acticle.setCreateBy(1l);
            acticle.setCreateTime(LocalDateTime.now());
            acticle.setModifyBy(1l);
            acticle.setModifyTime(LocalDateTime.now());
            acticleMapper.save(acticle);
            System.out.println("acticle="+acticle);
            return acticle;
        }
    
        /**
         * 删除指定key 的缓存
         * beforeInvocation=false  缓存的清除是否在方法之前执行
         * 默认是在方法之后执行
         * @param id
         * @return
         * @throws Exception
         */
        @CacheEvict(key = "#id",beforeInvocation = true)
        public int del(Long id) throws Exception {
            int isDel = 0;
            isDel = acticleMapper.del(id);
            return isDel;
        }
    
        /**
         * 删除所有缓存
         * @return
         * @throws Exception
         */
        @CacheEvict(allEntries = true)
        public int delAll() throws Exception {
            return 1;
        }
    
        @CachePut(key = "#result.id" ,unless = "#result.id == null" )
        public Acticle update(Acticle acticle) throws Exception {
            acticle.setModifyBy(1l);
            acticle.setModifyTime(LocalDateTime.now());
            return acticleMapper.update(acticle);
        }
    
        @Cacheable(key = "#id",condition = "#id > 0")
        public Acticle queryById(Long id) throws Exception {
            return acticleMapper.queryById(id);
        }
    
        /**
         * @Caching复杂组合缓存注解
         *
         * @param title
         * @return
         * @throws Exception
         */
        @Caching(cacheable = { @Cacheable(key = "#title")},
                put = {@CachePut(key = "#result.id"),
    //            @CachePut(key = "T(String).valueOf(#page).concat('-').concat(#pageSize)")
                @CachePut(key = "T(String).valueOf('tag').concat('-').concat(#result.tagId)")
        })
        public Acticle queryByTitle(String title) throws Exception {
            return acticleMapper.queryByTitle(title);
        }
    
        @Cacheable(key = "T(String).valueOf('tag').concat('-').concat(#tagId)")
        public Acticle queryByTag(Long tagId) throws Exception {
            return null;
        }
    }
    

    缓存工作原理

    当我们引入缓存的时候,SpringBoot的缓存自动配置CacheAutoConfiguration就会生效

    • CacheAutoConfiguration 中的CacheConfigurationImportSelector会导入很多缓存组件配置类
    • 通过debug看源码,知道imports 的内容如下
    static class CacheConfigurationImportSelector implements ImportSelector {
        CacheConfigurationImportSelector() {
        }
    
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            CacheType[] types = CacheType.values();
            String[] imports = new String[types.length];
    
            for(int i = 0; i < types.length; ++i) {
                imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
            }
    
            return imports;
        }
    }
    
    /**
    imports 如下
    
    0 = "org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration"
    1 = "org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration"
    2 = "org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration"
    3 = "org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration"
    4 = "org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration"
    5 = "org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration"
    6 = "org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration"
    7 = "org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration"
    8 = "org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration"     
    9 = "org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration"
    */
    
    
    • 如果没有引Redis, 则SimpleCacheConfiguration这个就是默认的配置类
    • SimpleCacheConfiguration注入了一个ConcurrentMapCacheManager
    • ConcurrentMapCacheManager 实现了CacheManager 接口
    • ConcurrentMapCacheManager 通过 ConcurrentHashMap 把数据缓存起来
    • CacheManager 有一个Cache getCache(String var1) 方法换取缓存
    • 流程大概就这里,感兴趣的同学可以打断点阅读源码

    本章代码示例地址

    相关文章

      网友评论

        本文标题:SpringBoot2与RedisCacheManager整合

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