栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Redis缓存在SpringBoot工程中的应用

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Redis缓存在SpringBoot工程中的应用

目录

一、通用配置(配置Value值的序列化方式)

二、基于RedisTemplate方式

1 查询业务

1.1 业务描述

1.2 业务逻辑代码

2 新增业务

2.1 业务描述

2.2 业务逻辑代码

3、自定义配置类

3.1 自定义RedisTemplate的泛型

3.2 自定义RedisTemplate中的序列化方式

二、基于AOP方式

1、查询业务

1.1 业务描述

1.2 业务逻辑代码

2、新增业务

2.1 业务描述

2.2 业务逻辑代码

3、基于ID更新(测试存取值)

3.1 业务描述

3.2 业务逻辑代码

4、基于ID查询(测试存取值)

4.1 业务描述

4.2 业务逻辑代码

5、自定义配置类

5.1 定义key生成器

5.2 定义cache管理对象

三、加入本地缓存策略

1、业务描述

2、业务实现

2.1 构建本地缓存对象

2.2 构建一个任务调度方法,定时刷新本地缓存,统一数据。

2.3.1 任务调度方法启动方案一

2.3.2 任务调度方法启动方案二

2.4 业务代码


一、通用配置(配置Value值的序列化方式)

RedisTemlate方式和AOP方式的value值都采用此方式进行序列化。

    @Bean
    public Jackson2JsonRedisSerializer jsonRedisSerializer(){
        Jackson2JsonRedisSerializer jsonRedisSerializer=
                new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper=new ObjectMapper();
        objectMapper.setVisibility(
                PropertyAccessor.GETTER,
                JsonAutoDetect.Visibility.ANY);
        //激活默认类型(存储json时会添加类型信息到json串中)
        objectMapper.activateDefaultTyping(
                objectMapper.getPolymorphicTypevalidator(),
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY);
        jsonRedisSerializer.setObjectMapper(objectMapper);
        return jsonRedisSerializer;
    }

二、基于RedisTemplate方式

1 查询业务

1.1 业务描述

RedisTemplate方式实现业务操作优先查询redis缓存,如果redis缓存中没有数据再查询mysql数据库。

1.2 业务逻辑代码
    //注入RedisTemplate对象
    @Autowired
    private RedisTemplate redisTemplate;
    //业务处理
    @Override
    public List selectTags() {
        //1.从redis查询Tag信息,redis有则直接返回
        ValueOperations> valueOperations =
        redisTemplate.opsForValue();
        List tags=valueOperations.get("tags");
        if(tags!=null&&!tags.isEmpty())return tags;
        //2.从redis没有获取tag信息,查询mysql
        tags = tagMapper.selectList(null);
        //3.将从mysql查询到tag信息存储到redis
        valueOperations.set("tags", tags);
        //4.返回查询结果
        return tags;
   }

2 新增业务

2.1 业务描述

新增一个对象,保证数据一致性。

实现步骤:先更新MySQL数据库,再更新redis数据库(修改等操作可以基于此方式进行实现)。

2.2 业务逻辑代码
    @Override
    public void insertTag(Tag tag) {
        //把数据写入到mysql数据库
        tagMapper.insert(tag);
        //把redis数据库数据更新
        ValueOperations> vo = redisTemplate.opsForValue();
        String key =     
            "tagCache::com.jt.blog.service.TagServiceImpl::java.lang.reflect.Method";
        List list = vo.get(key);
        list.add(tag);
        vo.set(key,list);
    }

3、自定义配置类

3.1 自定义RedisTemplate的泛型

默认泛型为Object,实际业务中需要进行自定义配置。

默认为:RedisTemplate
修改为:RedisTemplate
其中:  ? extends Object  意为上界限定通配符

3.2 自定义RedisTemplate中的序列化方式

Redis容易默认采用的是JDK的序列化方式,非Java程序读取的情况下无法阅读容器中的对象。

具体代码如下:

 
    @Bean
    @ConditionalOnMissingBean(name = {"redisTemplate"})
    public RedisTemplate redisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        //定义RedisTemplate的泛型类型
        RedisTemplate template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        //定义redisTemplate对象的序列号方式
        //1、定义key的序列化方式
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);
        //2、定义value的序列化方式
        template.setValueSerializer(jsonRedisSerializer());
        template.setHashValueSerializer(jsonRedisSerializer());
        //3.修改redisTemplate默认样式
        template.afterPropertiesSet();//after表示除了序列化,其他原有特性不丢
        template.setEnableTransactionSupport(true);
        return template;
    }

二、基于AOP方式

AOP方式要注意定义key的值

1、查询业务

1.1 业务描述

AOP方式实现业务操作优先查询redis缓存,如果redis缓存中没有数据再查询mysql数据库。

1.2 业务逻辑代码

只需在业务方法上添加 @Cacheable 注解即可,value的值为redis容器中key的值。

注:@Cacheable 注解生效前提需要在启动类或者配置类上添加 @EnableCaching 注解。

    
    @Cacheable(value = "tagCache")
    @Override
    public List selectTags() {
        return tagMapper.selectList(null);
    }

2、新增业务 2.1 业务描述

新增一个对象,保证数据一致性。实现思路同样使用先更新MySQL数据库,再更新redis数据库。

实现步骤:采用 @CacheEvict注解 , 该注解的作用是定义切入点方法, 执行此注解描述的方法时,底层通过AOP方式执行redis数据的清除操作

2.2 业务逻辑代码

使用@CacheEvict注解时,方法需要一个返回值,该返回值会作为value的值存入到数据库。

***需要注意的是方法key值的定义,此处的key值如果与查询的key不同,则取到的数据不统一***

    //假如没有指定key,默认KeyGenerator会生成一个key
    @CacheEvict(
            value = "tagCache",
            allEntries = true, //删除key对应的所有数据
            beforeInvocation = false)//执行insert之后,删除缓存
    @Override
    public Tag insertTag(Tag tag) {
        tagMapper.insert(tag);
        return tag;
    }

3、基于ID更新(测试存取值)

3.1 业务描述

根据ID修改内容,

3.2 业务逻辑代码
    @CacheEvict(
            value = "tagCache",
            allEntries = true, //删除key对应的所有数据
            beforeInvocation = false)//执行insert之后,删除缓存
    @CachePut(value = "tagCache",key = "#tag.id")
    @Override
    public Tag update(Tag tag) {
        tagMapper.updateById(tag);
        return tag;
    }

4、基于ID查询(测试存取值)

4.1 业务描述

根据ID查询内容,由于基于ID更新存入到redis数据库的key和value与基于ID查询的一样,所以可以直接查询到更新的数据。

4.2 业务逻辑代码
    @CachePut(value = "tagCache",key = "#id")
    @Override
    public Tag findById(Long id) {
        return tagMapper.selectById(id);
    }

5、自定义配置类

5.1 定义key生成器

默认key可读性较差。

需要注意的是定义 KeyGenerator bean对象时配置类需要继承CachingConfigurerSupport 类。

    
    @Bean
    public KeyGenerator keyGenerator(){
        return (o, method, objects) -> {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder
                    .append(o.getClass().getName())
                    .append("::")
                    .append(method.getClass().getName());
            for (Object object : objects) { //方法没有参数就没有这个循环了
                stringBuilder.append(object);
            }
            return stringBuilder.toString();
        };
    }

5.2 定义cache管理对象

在AOP方式下使用的序列化方式默认也是JDK方式,需要转换为Json方式的话也需要自己配置。

 
    @Bean
    public CacheManager CacheManager(RedisConnectionFactory redisConnectionFactory){

        RedisCacheConfiguration config = RedisCacheConfiguration
                .defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(60))//设置key的有效时间,可根据业务需求设置
        //配置key的序列化
                .serializeKeysWith(RedisSerializationContext
                .SerializationPair
                .fromSerializer(new StringRedisSerializer()))
        //配置value的序列化
                .serializevaluesWith(RedisSerializationContext
                .SerializationPair
                .fromSerializer(jsonRedisSerializer()))
                .disableCachingNullValues();//不存储空值

        return RedisCacheManager
                .builder(redisConnectionFactory)
                .cacheDefaults(config)//修改默认对象
                .transactionAware()//启动默认事物
                .build();
    }

三、加入本地缓存策略 1、业务描述

查询优先查询本地缓存,如果本地缓存没有数据查询redis缓存, 如果redis缓存没有数据最后再查询数据库。 

2、业务实现

2.1 构建本地缓存对象

使用 CopyonWriteArrayList 对象,相对于 ArrayList 这种方式线程安全。

List tags = new CopyOnWriteArrayList<>();  //本地Cache

2.2 构建一个任务调度方法,定时刷新本地缓存,统一数据。
    private Timer timer;
    private void doTimeRefreshTask(){
        //构建一个任务调度对象
        timer = new Timer();
        //构建一个任务对象
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("===refresh timerTask===");
                tags.clear();
            }
        };
        //执行定时任务(测试每隔5秒执行一次)
        timer.schedule(timerTask,5000,5000);
    }

2.3.1 任务调度方法启动方案一

利用构造方法加载机制启动任务调度方法。

    //任务调度方案启动方案一
    public TagController(){
        doTimeRefreshTask();
    }

2.3.2 任务调度方法启动方案二

 利用spring中Bean对象的生命周期方法对任务调度方法进行启动和销毁。

    //任务调度方案2,启动
    //Spring中Bean对象的生命周期方法,Bean对象初始化时执行此方法
    @PostConstruct
    public  void doInit(){
        doTimeRefreshTask();
    }
    //任务调度方案2,销毁
    @PostConstruct
    //退出定时任务
    public void doDestory(){
        timer.cancel();
    }

2.4 业务代码

采用了双重校验的方法,保证线程安全,同时减少阻塞。

    @GetMapping
    public  List doSelectTags(){
       if(tags.isEmpty()) {
           synchronized (tags) {
               if(tags.isEmpty()) {
                   tags.addAll(tagService.selectTags());//1.redis,2.mysql
               }
           }
       }
       return tags;
    }
序列化:
狭义层面:将对象转换为字节(I/O 相当于序列化的子集)
广义层面:将对象转换为指定格式字符串(例如:json)
封装:
狭义层面:属性私有化,方法能公开则公开
广义层面:一个系统有哪些服务(子系统),一个服务有哪些模块,一个模块有哪些对象,一个对象有哪些属性

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/329041.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号