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); } }
分类:
SpringBoot
, Redis
标签:
SpringBoot
, Redis
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)