zoukankan      html  css  js  c++  java
  • SpringBoot(4) ------>整合CacheManager与Redis

    1、向pom文件中添加依赖

         <!--springboot中的redis依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>

    2、application.yml添加配置

    spring:
      redis:
        host: localhost     # Redis服务器地址
        database: 0         # Redis数据库索引(默认为0)
        port: 6379          # Redis服务器连接端口
        password: ld123456  # Redis服务器连接密码(默认为空)

    3、RedisConfig配置类

    package com.donleo.mybatis.config;
    
    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.cache.interceptor.KeyGenerator;
    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.RedisConnectionFactory;
    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 java.util.Arrays;
    
    /**
     * @author liangd
     * date 2020-12-04 16:15
     * code Redis配置类
     */
    @Configuration
    @EnableCaching
    public class RedisConfig extends CachingConfigurerSupport {
        /**
         * 配置redisTemplate 代替默认配置
         * @param redisConnectionFactory  RedisConnectionFactory
         * @return  RedisTemplate
         */
        @Bean
        public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<String, Object> template = new RedisTemplate<>();
            template.setConnectionFactory(redisConnectionFactory);
            Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper mapper = new ObjectMapper();
            mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            serializer.setObjectMapper(mapper);
    
            template.setValueSerializer(serializer);
            //使用StringRedisSerializer来序列化和反序列化redis的key值
            template.setKeySerializer(new StringRedisSerializer());
            template.afterPropertiesSet();
            return template;
        }
    
        /**
         *
         * 配置 cacheManager 代替默认的cacheManager (缓存管理器)
         * @param factory RedisConnectionFactory
         * @return  CacheManager
         */
        @Bean
        public CacheManager cacheManager(RedisConnectionFactory factory) {
            RedisSerializer<String> redisSerializer = new StringRedisSerializer();
            Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            serializer.setObjectMapper(objectMapper);
            // 配置序列化
            RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer));
    
            RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                    .cacheDefaults(config)
                    .build();
            return cacheManager;
        }
    
        /**
         * 配置KeyGenerator
         * @return
         */
        @Bean
        @Override
        public KeyGenerator keyGenerator() {
            //lambda 表达式
            return (target, method, params) -> method.getName() + Arrays.asList(params);
        }
        /*
            // 1. 不需要参数,返回值为 5
                () -> 5
    
            // 2. 接收一个参数(数字类型),返回其2倍的值
                x -> 2 * x
    
            // 3. 接受2个参数(数字),并返回他们的差值
                (x, y) -> x – y
    
            // 4. 接收2个int型整数,返回他们的和
                (int x, int y) -> x + y
    
            // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
                (String s) -> System.out.print(s)
         */
         /*   @Bean
           @Override
           public KeyGenerator keyGenerator() {
               return (target,  method,  params)->{
                   return method.getName()+ Arrays.asList(params);
               };
           }*/
    }

    4、自定义Redis工具类

      1)接口层

    package com.donleo.mybatis.service;
    
    /**
     * @author liangd
     * date 2020-12-04 16:19
     * code 自定义封装redis接口定义
     */
    public interface IRedisService {
        /**
         * 指定缓存失效时间
         * @param key 键
         * @param time 时间(秒)
         * @return
         */
        boolean expire(String key, long time);
    
        /**
         * 根据key 获取过期时间
         * @param key 键 不能为null
         * @return 时间(秒) 返回0代表为永久有效
         */
        long getExpire(String key);
    
        /**
         * 判断key是否存在
         * @param key 键
         * @return true 存在 false不存在
         */
    
        boolean hasKey(String key);
    
        /**
         * 删除缓存
         * @param key 可以传一个值 或多个
         */
    
        void del(String... key);
    
        /**
         * 普通缓存获取
         * @param key 键
         * @return*/
        Object get(String key);
    
        /**
         * 普通缓存放入
         * @param key 键
         * @param value 值
         * @return true成功 false失败
         */
        boolean set(String key, Object value);
    
        /**
         * 普通缓存放入并设置时间
         * @param key 键
         * @param value 值
         * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
         * @return true成功 false 失败
         */
        boolean set(String key, Object value, long time);
    }

      2)实现层

    package com.donleo.mybatis.service.impl;
    
    import com.donleo.mybatis.service.IRedisService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    import org.springframework.util.CollectionUtils;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author liangd
     * date 2020-12-04 16:21
     * code 自定义封装Redis接口定义
     */
    @Service
    public class RedisServiceImpl implements IRedisService {
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        /**
         * 指定缓存失效时间
         *
         * @param key  键
         * @param time 时间(秒)
         * @return
         */
        @Override
        public boolean expire(String key, long time) {
            try {
                if (time > 0) {
                    redisTemplate.expire(key, time, TimeUnit.SECONDS);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 根据key 获取过期时间
         *
         * @param key 键 不能为null
         * @return 时间(秒) 返回0代表为永久有效
         */
        @Override
        public long getExpire(String key) {
            return redisTemplate.getExpire(key, TimeUnit.SECONDS);
        }
    
        /**
         * 判断key是否存在
         *
         * @param key 键
         * @return true 存在 false不存在
         */
        @Override
        public boolean hasKey(String key) {
            try {
                return redisTemplate.hasKey(key);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 删除缓存
         *
         * @param key 可以传一个值 或多个
         */
        @Override
        @SuppressWarnings("unchecked")
        public void del(String... key) {
            if (key != null && key.length > 0) {
                if (key.length == 1) {
                    redisTemplate.delete(key[0]);
                } else {
                    redisTemplate.delete(CollectionUtils.arrayToList(key));
                }
            }
        }
    
        //============================String=============================
    
        /**
         * 普通缓存获取
         *
         * @param key 键
         * @return*/
        @Override
        public Object get(String key) {
            return key == null ? null : redisTemplate.opsForValue().get(key);
        }
    
        /**
         * 普通缓存放入
         *
         * @param key   键
         * @param value 值
         * @return true成功 false失败
         */
        @Override
        public boolean set(String key, Object value) {
            try {
                redisTemplate.opsForValue().set(key, value);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        /**
         * 普通缓存放入并设置时间
         *
         * @param key   键
         * @param value 值
         * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
         * @return true成功 false 失败
         */
        @Override
        public boolean set(String key, Object value, long time) {
            try {
                if (time > 0) {
                    redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
                } else {
                    set(key, value);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    }

    5、测试-->通过Redis工具类缓存

    package com.donleo.mybatis.service.impl;
    
    import com.donleo.mybatis.common.CommonResult;
    import com.donleo.mybatis.dao.IDeptMapper;
    import com.donleo.mybatis.model.Dept;
    import com.donleo.mybatis.service.IDeptService;
    import com.donleo.mybatis.service.IRedisService;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    /**
     * @author liangd
     * date 2020-12-03 11:09
     * code 部门逻辑层(使用自定义RedisTemplate设置缓存)
     */
    @Service
    public class DeptServiceImpl implements IDeptService {
    
        @Resource
        private IDeptMapper deptMapper;
    
        @Resource
        private IRedisService redisService;
    
        @Override
        public CommonResult findAll() {
            List<Dept> list;
            boolean b = redisService.hasKey("deptList:");
            if (b) {
                Object object = redisService.get("deptList:");
                list = (List<Dept>) object;
            } else {
                list = deptMapper.selectAll();
                redisService.set("deptList:",list);
            }
            return CommonResult.success(list);
        }
    
        @Override
        public Integer add(Dept dept) {
            dept.setId(null);
            deptMapper.insert(dept);
            return dept.getId();
        }
    
        @Override
        public Integer delete(Integer id) {
            redisService.del("dept:"+id);
            redisService.del("deptList:");
            return deptMapper.deleteByPrimaryKey(id);
        }
    
        @Override
        public Integer update(Dept dept) {
            return deptMapper.updateByPrimaryKeySelective(dept);
        }
    
        @Override
        public Dept findById(Integer id) {
            //判断redis中是否存在当前key,加一个冒号生成文件夹
            boolean b = redisService.hasKey("dept:" + id);
            Dept dept;
            //如果存在,从redis中查询,否则从数据库中查询
            if (b) {
                dept = (Dept) redisService.get("dept:" + id);
            } else {
                dept = deptMapper.selectByPrimaryKey(id);
                //放入resis中
                redisService.set("dept:"+id, dept);
            }
            return dept;
        }
    }

    6、测试-->通过CacheManager缓存(1)

    package com.donleo.mybatis.service.impl;
    
    import com.donleo.mybatis.common.CommonResult;
    import com.donleo.mybatis.dao.IRoleMapper;
    import com.donleo.mybatis.model.Role;
    import com.donleo.mybatis.service.IRoleService;
    import org.springframework.cache.annotation.*;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    /**
     * @author liangd
     * date 2020-12-04 17:42
     * code 角色逻辑实现层(使用注解CacheManager设置缓存)
     * 使用@CacheConfig配置缓存名
     */
    @Service
    @CacheConfig(cacheNames = "role")
    public class RoleServiceImpl implements IRoleService {
    
        @Resource
        private IRoleMapper roleMapper;
    
        /**
         * 使用@Caching注解可以让我们在一个方法或者类上同时指定多个Spring Cache相关的注解。
         * 其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict。
         *
         * @param role
         * @return
         */
        @Override
        @Caching(
                put = @CachePut(key = "#role.id"),
                evict = @CacheEvict(key = "'findAllRole[]'")
        )
        public CommonResult add(Role role) {
            role.setId(null);
            try {
                roleMapper.insert(role);
            } catch (Exception e) {
                return CommonResult.failed();
            }
            return CommonResult.success(role.getId());
        }
    
        /**
         * 使用@CacheEvict移除key
         * @param id
         * @return
         */
        @Override
        @CacheEvict(key = "#id")
        public Integer delete(Integer id) {
            return roleMapper.deleteByPrimaryKey(id);
        }
    
        @Override
        public Integer update(Role role) {
            return roleMapper.updateByPrimaryKeySelective(role);
        }
    
        /**
         * 使用@Cacheable标注的方法,Spring在每次执行前都会检查Cache中是否存在相同key的缓存元素,
         * 如果存在就不再执行该方法,而是直接从缓存中获取结果进行返回,否则才会执行并将返回结果存入指定的缓存中。
         *
         * @param id
         * @return
         */
        @Override
        @Cacheable(key = "#id")
        public Role findById(Integer id) {
            return roleMapper.selectByPrimaryKey(id);
        }
    
        /**
         * 使用@CachePut 也可以声明一个方法支持缓存功能。
         * 与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,
         * 而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。
         *
         * @return
         * keyGenerator将方法名作为key,存进缓存,例如findAllRole[],删除key时需要添加单引号
         */
        @Override
        @CachePut(keyGenerator = "keyGenerator")
        public CommonResult findAllRole() {
            List<Role> list = roleMapper.selectAll();
            return CommonResult.success(list);
        }
    }

    7、测试-->通过CacheManager缓存(2)

    package com.donleo.cache.service.impl;
    
    import com.donleo.cache.common.CommonResult;
    import com.donleo.cache.mapper.IRoleMapper;
    import com.donleo.cache.model.Role;
    import com.donleo.cache.service.IRoleService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cache.annotation.*;
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    /**
     * @author liangd
     * date 2020-12-10 09:49
     * code
     * .@CacheConfig抽取缓存的公共配置,在这里配置了cacheNames就不需要再每个方法上面指定value属性了
     */
    @Service
    @CacheConfig(cacheNames = "role")
    public class RoleServiceImpl implements IRoleService {
    
        @Autowired
        private IRoleMapper roleMapper;
    
        /**
         * .@Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,
         * 如果没有就运行方法并将结果放入缓存;以后再来调用就可以直接使用缓存中的数据;
         *
         * .@Cacheable 将方法的运行结果进行缓存,以后再要相同的数据,直接从缓存中获取,不用调用方法;
         * <p>
         * CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一-个缓存组件有自己唯- --一个名字;
         * <p>
         * <p>
         * 原理:
         * 1、自动配置类 CacheAutoConfiguration
         * 2、缓存的配置类
         * org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
         * org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
         * org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
         * org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
         * org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
         * org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
         * org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
         * org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
         * org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration
         * org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration【默认】
         * org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
         * <p>
         * 3、哪个配置类默认生效:SimpleCacheConfiguration;
         * <p>
         * 4、给容器中注册了一个CacheManager:ConcurrentMapCacheManager
         * 5、可以获取和创建ConcurrentMapCache类型的缓存组件;他的作用将数据保存在ConcurrentMap中;
         * <p>
         * 运行流程:
         * <p>
         * .@Cacheable:
         * 1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;
         *  (CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。
         * 2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;
         *      key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key;
         *      SimpleKeyGenerator生成key的默认策略;
         *          如果没有参数;key=new SimpleKey();
         *          如果有一个参数:key=参数的值
         *          如果有多个参数:key=new SimpleKey(params);
         * 3、没有查到缓存就调用目标方法;
         * 4、将目标方法返回的结果,放进缓存中
         * <p>
         * <p>
         * 核心:
         * 1)、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件
         * 2)、key使用keyGenerator生成的,默认是SimpleKeyGenerator
         * <p>
         * 几个属性:
         * a)cacheNames/value:指定缓存组件的名字
         *      cacheNames = {"role"}可以使用多个参数,是数组的形式,可以指定多个缓存
         * b)key:缓存数据使用的key,可以用他来指定。默认是使用方法参数的值
         *      编写SpEl:   #id  #a0,#po,#argrs[0]  "0"代表参数的索引
         *      #result  方法执行后的返回值
         *      #root.methodName   方法名
         *      key = "#root.methodName+'['+#id+']'"
         * c)keyGenerator: key的生成器、可以自己指定key的生成器的组件Id
         *      key/keyGenerator 二选一
         *      keyGenerator = "myKeyGenerator"
         * d)cacheManager:指定缓存管理器或者cacheResolver:获取解析器
         *      cacheManager/cacheResolver 二选一
         * e)condition:指定符合缓存的条件
         *      condition = "#id>0 and #root.methodName eq 'aaa'" 可以多条件判断
         * f)unless: 否定缓存,当unless的条件为true,方法结果不会被缓存,可以获取结果进行判断
         *      unless = "#result==null",结果为null,就不缓存
         * g)sync:是否使用异步模式
         *      默认false   同步
         *      为true时,unless不支持
         */
    
        @Override
        @Cacheable(value = {"role"}, key = "#id",
                condition = "#id>0", unless = "#result==null")
        public Role findById(Integer id) {
            return roleMapper.selectById(id);
        }
    
        /**
         * .@CachePut既调用方法、又更新数据,达到同步更新缓存
         * <p>
         * 运行时机:
         * 1、先调用目标方法
         * 2、将目标方法的结果缓存起来
         *
         * 条件:存取Id的key要保持一致
         *     key = "#role.id"     传入员工的Id
         *     key = "#result.id"   使用返回员工的Id
         * 注意: @Cacheable不能使用#result
         *      因为 @Cacheable在目标方法执行之前需要得到这个key,所以不能用#result
         */
        @Override
        @CachePut(value = "role", key = "#result.id")
        public Role update(Role role) {
            roleMapper.updateById(role);
            return role;
        }
    
        /**
         * .@CacheEvict 缓存清除
         *
         *  key:指定要清除的数据
         *  allEntries:指定清除这个缓存库的所有数据,默认为false
         *  beforeInvocation:在执行方法之前清除,默认为false,在方法之后执行
         *
         */
        @Override
        @CacheEvict(/*value = "role",*/key = "#id")
        public Integer delete(Integer id) {
            return roleMapper.deleteById(id);
        }
    
        /**
         * .@Caching 定义复杂缓存规则
         */
        @Override
        @Caching(
                cacheable = {
                        @Cacheable(key = "#role.roleName")
                },
                put = {
                        @CachePut(key = "#role.id"),
                        @CachePut(key = "#role.roleCode")
                }
        )
        public CommonResult add(Role role) {
            role.setId(null);
            try {
                roleMapper.insert(role);
            } catch (Exception e) {
                return CommonResult.failed();
            }
            return CommonResult.success(role.getId());
        }
    
        @Override
        public CommonResult findAllRole() {
            List<Role> roleList = roleMapper.selectList(null);
            return CommonResult.success(roleList);
        }
    }
    作者:donleo123
    本文如对您有帮助,还请多推荐下此文,如有错误欢迎指正,相互学习,共同进步。
  • 相关阅读:
    Real-Time SQL Monitoring
    MySQL数据复制的校验
    Mysql复制-Slave库设置复制延迟
    MySQL Replication的相关文件
    mysql 启动和关闭外键约束
    mysql写注释的几种方法
    salt更换新key
    saltstack之syndic的配置
    salt-ssh的批量脚本及使用方法
    koan重装system
  • 原文地址:https://www.cnblogs.com/donleo123/p/14286977.html
Copyright © 2011-2022 走看看