当前位置: 首页 > news >正文

Spring cache整合Redis

参考资料:

参考视频

Mybatis-Plus搭建教程

Spring-cache整合流程

Spring-cache官网

参考demo


Spring cache使用介绍:

简介

        Spring cache简化了数据库缓存的同步过程,在绝大多数项目中都有应用

        除redis外,Spring cache还可以整合Caffeine、EhCache等

重要注解介绍

        Spring cache重要的注解如下:

注解说明
@EnableCaching开启缓存注解功能
@Cacheable

在方法执行前先查看缓存中是否有数据,

如果有数据,则直接返回缓存数据;

若没有数据,调用方法并将方法返回值放到缓存中

@CachePut向缓存中存入数据
@CacheEvict从缓存中删除数据

        上述这些注解,key属性支持SPEL语法,用于标记入参或回参中,某个或者某几个属性

SPEL语法

        PEL语法以#作为标记的开始

指定入参中的属性
表达式例子
参数名#{参数名}.{属性1}.{属性1.1}.{属性1.1.1}.......#student.id
参数位置#p{index}.{属性1}.{属性1.1}.{属性1.1.1}.......#p0.id
参数名(多参数或关系)#{参数名1}.{属性1}.{属性1.1}_#{参数名2}.{属性2}.{属性2.1}_......#student.id_#student.name_#user.id
参数位置(多参数或关系)#p{index1}.{属性1}.{属性1.1}_#p{index2}.{属性2}.{属性2.1}_......#p0.id_#p0.name_#p1.id
  •  以参数名作为入口
key = "#id"

key = "#student.id"

指的是student中的id元素

key = "#result.body.id"
  • 以参数的位置作为入口 

        通常是#p+参数的index,如:#p0表示第一个参数,#p1表示第二个参数......

key = "#p0"

key = "#p0.id"

第一个元素中的id元素

  • 指定多个属性,关系为或
key = "#p0.id+'_'+#p0.name+'_'+#p0.age"
key = "#student.id+'_'+#student.name+'_'+#student.age"

指的是student中的id或name或者age属性

指定回参中的属性
表达式例子
#result.{属性1}.{属性1.1}.{属性1.1.1}

#result.body.id

指的是返回值中的body元素中的id元素

其他常用的属性

allEntries

        某个父标签下的所有子标签,如:

    @CacheEvict(value = "student", allEntries = true)

        删除student下的所有子标签

unless

        unless : 表示满足条件则不缓存,如:

@Cacheable(value = "student",key = "#id",unless = "#result == null")

    

如果返回值是null则不缓存

condition

        condition : 表示满足什么条件, 再进行缓存 ,如:

@Cacheable(value = "student",key = "#p0",condition = "#id.length() > 3")

如果返回值的id长度大于3才进行缓存


编写案例:

创建数据库并整合Mybatis-plus

首先创建数据库

CREATE TABLE `student`  (`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,`age` int(3) NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

并且使用Mybatis或者Mybatis-Plus整合到SpringBoot中,参考网址

进行相关配置

pom中添加如下依赖:

<!-- 只引入所需模块 --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-http</artifactId><version>5.8.12</version></dependency><!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

application.yml中添加如下配置

server:port: 8081
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8username: rootpassword: 123888# Redis 配置redis:host: 192.168.14.53port: 6379password: nulldatabase: 0timeout: 10000 # 超时设置,毫秒jedis:pool:max-active: 10 # 最大连接数max-idle: 5    # 最大空闲连接数min-idle: 1    # 最小空闲连接数# 缓存配置(使用 Redis)cache:type: 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.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.*;import java.lang.reflect.Method;
import java.time.Duration;/*** Redis+Cache配置类*/
@Configuration
public class RedisConfig {/*** 自定义key规则* @return*/@Beanpublic KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic Object generate(Object target, Method method, Object... params) {StringBuilder sb = new StringBuilder();sb.append(target.getClass().getName());sb.append(method.getName());for (Object obj : params) {sb.append(obj.toString());}return sb.toString();}};}/*** 设置RedisTemplate规则* @param redisConnectionFactory* @return*/@Beanpublic RedisTemplate<String, Object> redisTemplate(org.springframework.data.redis.connection.RedisConnectionFactory redisConnectionFactory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);template.setKeySerializer(new StringRedisSerializer()); // key序列化template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // value序列化return template;}/*** 设置CacheManager缓存规则* @param factory* @return*/@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {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);// 配置序列化(解决乱码的问题),过期时间600秒RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).disableCachingNullValues();RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();return cacheManager;}}

相关接口的编写

        增加接口

@RequestMapping("add")public ResponseEntity<Student> add(@RequestBody Student student) {Student saved = addAndCache(student);return ResponseEntity.ok(saved);}@CachePut(value = "student", key = "#result.id")public Student addAndCache(Student student) {studentService.save(student);return student;}

        删除-单个接口

@RequestMapping("deleteById")//  删除单个缓存//@CacheEvict(value = "student",key = "#id")@CacheEvict(value = "student",key = "#p0")public ResponseEntity<String> deleteById(@RequestParam("id")String  id){studentService.removeById(id);return ResponseEntity.ok("删除成功");}

        删除-全部接口

@RequestMapping("deleteAll")//  删除student下的全部缓存@CacheEvict(value = "student", allEntries = true)public ResponseEntity<String> deleteAll(){studentService.remove(null);return ResponseEntity.ok("删除成功");}

        更新接口

@RequestMapping("update")//  删除单个缓存//@CacheEvict(value = "student",key = "#student.id")@CacheEvict(value = "student",key = "#p0.id")public ResponseEntity<Student> update(@RequestBody Student student){studentService.updateById(student);return ResponseEntity.ok(student);}

        查询-单个接口

@RequestMapping("getById")@Cacheable(value = "student",key = "#id")//@Cacheable(value = "student",key = "#p0")//@Cacheable(value = "student",key = "#id",unless = "#result == null")//@Cacheable(value = "student",key = "#p0",condition = "#id.length() > 3")public ResponseEntity<Student> getById(@RequestParam("id")String  id){Student student = studentService.getById(id);return ResponseEntity.ok(student);}

        查询-多个接口

@RequestMapping("getList")//@Cacheable(value = "student",key = "#student.id+'_'+#student.name+'_'+#student.age")@Cacheable(value = "student",key = "#p0.id+'_'+#p0.name+'_'+#p0.age")public ResponseEntity<List<Student>> getList(@RequestBody Student student){LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();wrapper.eq(Student::getId,student.getId()).eq(Student::getName,student.getName()).eq(Student::getAge,student.getAge());List<Student> list = studentService.list(wrapper);return ResponseEntity.ok(list);}

总体的代码如下:

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.springcachedemo.entity.Student;
import com.example.springcachedemo.service.StudentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@Slf4j
public class Controller {@Autowiredprivate StudentService studentService;@RequestMapping("add")public ResponseEntity<Student> add(@RequestBody Student student) {Student saved = addAndCache(student);return ResponseEntity.ok(saved);}@CachePut(value = "student", key = "#result.id")public Student addAndCache(Student student) {studentService.save(student);return student;}@RequestMapping("deleteById")//  删除单个缓存//@CacheEvict(value = "student",key = "#id")@CacheEvict(value = "student",key = "#p0")public ResponseEntity<String> deleteById(@RequestParam("id")String  id){studentService.removeById(id);return ResponseEntity.ok("删除成功");}@RequestMapping("deleteAll")//  删除student下的全部缓存@CacheEvict(value = "student", allEntries = true)public ResponseEntity<String> deleteAll(){studentService.remove(null);return ResponseEntity.ok("删除成功");}@RequestMapping("update")//  删除单个缓存//@CacheEvict(value = "student",key = "#student.id")@CacheEvict(value = "student",key = "#p0.id")public ResponseEntity<Student> update(@RequestBody Student student){studentService.updateById(student);return ResponseEntity.ok(student);}@RequestMapping("getById")@Cacheable(value = "student",key = "#id")//@Cacheable(value = "student",key = "#p0")//@Cacheable(value = "student",key = "#id",unless = "#result == null")//@Cacheable(value = "student",key = "#p0",condition = "#id.length() > 3")public ResponseEntity<Student> getById(@RequestParam("id")String  id){Student student = studentService.getById(id);return ResponseEntity.ok(student);}@RequestMapping("getList")//@Cacheable(value = "student",key = "#student.id+'_'+#student.name+'_'+#student.age")@Cacheable(value = "student",key = "#p0.id+'_'+#p0.name+'_'+#p0.age")public ResponseEntity<List<Student>> getList(@RequestBody Student student){LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();wrapper.eq(Student::getId,student.getId()).eq(Student::getName,student.getName()).eq(Student::getAge,student.getAge());List<Student> list = studentService.list(wrapper);return ResponseEntity.ok(list);}}

Spring-cache整合布隆过滤器

        布隆过滤器作为防止缓存穿透或者缓存击穿的主要方案,在很多项目中都有应用

        下面是基于Redission在上面的基础上做的布隆过滤器

  • pom中添加Redission的依赖
        <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.21.3</version></dependency>

  • 添加Redision和布隆过滤器相关的配置

import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfig {public static final String BLOOM_FILTER_KEY = "myBloom";@Beanpublic RedissonClient redissonClient() {Config config = new Config();config.useSingleServer().setAddress("redis://192.168.14.12:6379").setDatabase(0);return Redisson.create(config);}@Beanpublic RBloomFilter<String> studentBloomFilter(RedissonClient redissonClient) {RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(BLOOM_FILTER_KEY);bloomFilter.tryInit(1_000_000L, 0.01); // 预估 100w 数据,误判率 1%return bloomFilter;}
}
  • 注入布隆过滤器的判断方法

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
@Slf4j
public class StudentBloomService {@Autowiredprivate RBloomFilter<String> studentBloomFilter;/*** 判断 studentId 是否可能存在*/public boolean mightExist(String id) {log.info("bloom filter 判断 id 是否可能存在: {}", id);return studentBloomFilter.contains(id);}
}
  • 在相关的接口中添加布隆过滤器

        需要注意的是:布隆过滤器的元素,只能增加,不能删除,只能重建

        添加接口改为:

@RequestMapping("add")public ResponseEntity<Student> add(@RequestBody Student student) {Student saved = addAndCache(student);// 插入布隆过滤器studentBloomFilter.add(String.valueOf(student.getId()));return ResponseEntity.ok(saved);}@CachePut(value = "student", key = "#result.id")public Student addAndCache(Student student) {studentService.save(student);return student;}

        查询接口改为:

@RequestMapping("getById")//@Cacheable(value = "student",key = "#id")//@Cacheable(value = "student",key = "#p0")//@Cacheable(value = "student",key = "#id",unless = "#result == null")//@Cacheable(value = "student",key = "#p0",condition = "#id.length() > 3")@Cacheable(value = "student", key = "#id", condition = "@studentBloomService.mightExist(#id)")public ResponseEntity<Student> getById(@RequestParam("id")String  id){Student student = studentService.getById(id);return ResponseEntity.ok(student);}

  • 总的代码为:

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.springcachedemo.entity.Student;
import com.example.springcachedemo.service.StudentService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@Slf4j
@RequestMapping("bloom")
public class BloomController {@Autowiredprivate StudentService studentService;@Autowiredprivate RBloomFilter<String> studentBloomFilter;@RequestMapping("add")public ResponseEntity<Student> add(@RequestBody Student student) {Student saved = addAndCache(student);// 插入布隆过滤器studentBloomFilter.add(String.valueOf(student.getId()));return ResponseEntity.ok(saved);}@CachePut(value = "student", key = "#result.id")public Student addAndCache(Student student) {studentService.save(student);return student;}@RequestMapping("deleteById")//  删除单个缓存//@CacheEvict(value = "student",key = "#id")@CacheEvict(value = "student",key = "#p0")public ResponseEntity<String> deleteById(@RequestParam("id")String  id){studentService.removeById(id);// ⚠️ 注意:布隆过滤器 **不能删除**,只能重建!return ResponseEntity.ok("删除成功");}@RequestMapping("deleteAll")//  删除student下的全部缓存@CacheEvict(value = "student", allEntries = true)public ResponseEntity<String> deleteAll(){studentService.remove(null);// ⚠️ 注意:布隆过滤器 **不能删除**,只能重建!return ResponseEntity.ok("删除成功");}@RequestMapping("update")//  删除单个缓存//@CacheEvict(value = "student",key = "#student.id")@CacheEvict(value = "student",key = "#p0.id")public ResponseEntity<Student> update(@RequestBody Student student){studentService.updateById(student);// ⚠️ 注意:布隆过滤器 **不能删除**,只能重建!return ResponseEntity.ok(student);}@RequestMapping("getById")//@Cacheable(value = "student",key = "#id")//@Cacheable(value = "student",key = "#p0")//@Cacheable(value = "student",key = "#id",unless = "#result == null")//@Cacheable(value = "student",key = "#p0",condition = "#id.length() > 3")@Cacheable(value = "student", key = "#id", condition = "@studentBloomService.mightExist(#id)")public ResponseEntity<Student> getById(@RequestParam("id")String  id){Student student = studentService.getById(id);return ResponseEntity.ok(student);}@RequestMapping("getList")//@Cacheable(value = "student",key = "#student.id+'_'+#student.name+'_'+#student.age")@Cacheable(value = "student",key = "#p0.id+'_'+#p0.name+'_'+#p0.age")public ResponseEntity<List<Student>> getList(@RequestBody Student student){LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();wrapper.eq(Student::getId,student.getId()).eq(Student::getName,student.getName()).eq(Student::getAge,student.getAge());List<Student> list = studentService.list(wrapper);return ResponseEntity.ok(list);}}

http://www.dtcms.com/a/428771.html

相关文章:

  • 网站做伪静态知识付费微网站开发
  • 【从零开始java学习|第二十一篇】包装类是干嘛的
  • 网站建设运营协议书子公司网站备案
  • 晋江市建设招投标网站自己怎么制作网页游戏
  • 衡水建立网站关键词排名优化易下拉稳定
  • 国外网站建设软件排行榜h5快速建站
  • 长沙 php企业网站系统一个公司可以备案几个网站
  • 基本信息型网站有哪些爱做网站免费模板vip
  • 咖啡网站设计模板河北手机版建站系统开发
  • 什么是烟雾病?从症状识别到治疗选择
  • C++内存泄漏排查:从基础到高级的完整工具指南
  • [MT6835] MT6835如何通过指令判断secureboot是否开启
  • 互联网信息服务算法备案深度解析:从适用对象到风险警示的科普指南
  • svn: E160028
  • 网站放到云服务器上怎么做哪个网站可以做砍价
  • Memcached 安装与服务管理指南
  • 少年三国志(本地部署游玩)
  • 凡科做网站不好网络服务公司
  • 闲置tp路由自己做网站怎么在国税网站上做实名认证吗
  • 呼市賽罕区信息网站做一顿饭工作安徽六安
  • 手机评测网站设计师网单怎么做
  • sshd 启动失败问题排查总结(没有core)
  • 网站的页面由什么组成中铁建设集团有限公司招聘官网
  • 【Rust GUI开发入门】编写一个本地音乐播放器(7. 制作歌词显示面板)
  • dedecms做地方网站中建八局第二建设有限公司
  • 胶州网站建设平台外贸出口新三样
  • TransmittableThreadLocal(父子线程传递ThreadLocal)
  • 做的比较漂亮的网站门户网站编辑流程
  • 北京通网站建设一 网站建设方案
  • 网线介绍、家庭测网速方法、网线接法、水晶头接法