Redis缓存在SpringBoot工程中的应用

编程入门 行业动态 更新时间:2024-10-09 18:20:04

Redis<a href=https://www.elefans.com/category/jswz/34/1771061.html style=缓存在SpringBoot工程中的应用"/>

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值都采用此方式进行序列化。

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;@Configuration
public class RedisConfig {@Beanpublic 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对象@Autowiredprivate RedisTemplate redisTemplate;//业务处理@Overridepublic List<Tag> selectTags() {//1.从redis查询Tag信息,redis有则直接返回ValueOperations<String,List<Tag>> valueOperations =redisTemplate.opsForValue();List<Tag> tags=valueOperations.get("tags");if(tags!=null&&!tags.isEmpty())return tags;//2.从redis没有获取tag信息,查询mysqltags = tagMapper.selectList(null);//3.将从mysql查询到tag信息存储到redisvalueOperations.set("tags", tags);//4.返回查询结果return tags;}

2 新增业务

2.1 业务描述

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

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

2.2 业务逻辑代码

    @Overridepublic void insertTag(Tag tag) {//把数据写入到mysql数据库tagMapper.insert(tag);//把redis数据库数据更新ValueOperations<String,List<Tag>> 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<Object,Object>
修改为:RedisTemplate<String, ? extends Object>
其中:  ? extends Object  意为上界限定通配符

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

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

具体代码如下:

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
public class RedisConfig {
/*** 1、自定义RedisTemplate的泛型* 2、自定义RedisTemplate中的序列化方式* 代码定制参考RedisAutoConfiguration类*/@Bean@ConditionalOnMissingBean(name = {"redisTemplate"})public RedisTemplate<String, ? extends Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {//定义RedisTemplate的泛型类型RedisTemplate<String, ? extends Object> 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注解描述的方法为缓存切入点方法* 访问此方法时系统底层会先从缓存查找数据,假如缓存没有会查询mysql* 这个注解假如想生效需要在启动类或者配置上添加@EnableCaching注解*/@Cacheable(value = "tagCache")@Overridepublic List<Tag> selectTags() {return tagMapper.selectList(null);}

2、新增业务

2.1 业务描述

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

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

2.2 业务逻辑代码

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

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

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

3.1 业务描述

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

根据ID修改内容,

3.2 业务逻辑代码

    @CacheEvict(value = "tagCache",allEntries = true, //删除key对应的所有数据beforeInvocation = false)//执行insert之后,删除缓存@CachePut(value = "tagCache",key = "#tag.id")@Overridepublic 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")@Overridepublic Tag findById(Long id) {return tagMapper.selectById(id);}

5、自定义配置类

5.1 定义key生成器

默认key可读性较差。

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


import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedisConfig {/*** 自定义缓存key的生成器,不定义也可以使用默认的key* 注:定义KeyGenerator对象key生成器时,配置类需要继承CachingConfigurerSupport对象*/@Beanpublic 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方式的话也需要自己配置。

import org.springframework.cache.CacheManager;
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.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;@Configuration
public class RedisConfig {/*** 自定义Cache管理器对象,按照指定的序列化方式进行序列化,* 将序列化修改为Json方式(redis默认的序列化方式是JDK序列化)* redis默认的序列化方式是JDK序列化*/@Beanpublic 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<Tag> tags = new CopyOnWriteArrayList<>();  //本地Cache

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

    private Timer timer;private void doTimeRefreshTask(){//构建一个任务调度对象timer = new Timer();//构建一个任务对象TimerTask timerTask = new TimerTask() {@Overridepublic 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对象初始化时执行此方法@PostConstructpublic  void doInit(){doTimeRefreshTask();}//任务调度方案2,销毁@PostConstruct//退出定时任务public void doDestory(){timer.cancel();}

2.4 业务代码

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

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

更多推荐

Redis缓存在SpringBoot工程中的应用

本文发布于:2024-02-06 09:11:41,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1748117.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:缓存   工程   Redis   SpringBoot

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!