Springboot+Redis优雅的实现分布式缓存附使用示例(redisTemplate及Annotation)(附源码下载)
Springboot-cli 开发脚手架系列
Springboot系列:Springboot+Redis优雅的实现分布式缓存(redisTemplate及Annotation)
文章目录
前言
致力于让开发者快速搭建基础环境并让应用跑起来,并提供使用示例供使用者参考,快速上手。
本博客项目源码地址:
- 项目源码github地址
- 项目源码国内gitee地址
1. 环境
- 引入依赖
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.10.0</version> </dependency>
application.yml
spring: # redis 配置 redis: database: 0 host: 127.0.0.1 port: 6379 #连接超时时间 timeout: 3000 password: 123456 #连接池配置 lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: -1
2. 配置redisTemplate
- 编写
RedisConfig.java
配置,配置序列化方式
@Configuration@EnableCachingpublic class RedisConfig extends CachingConfigurerSupport { /** * RedisTemplate 默认使用 jdk 序列化,存在乱码等问题,将 value 的序列化方式换为 Jackson 后,value 中不再出现乱码。 */ @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); template.setKeySerializer(RedisSerializer.string()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setEnableTransactionSupport(true); return template; }}
- 封装
RedisUtils.java
/** * Redis工具类 */@Component@Slf4jpublic class RedisUtils { @Resource private RedisTemplate<String, Object> rt; private static RedisTemplate<String, Object> redisTemplate; @PostConstruct public void init() { // bean赋值给静态变量 redisTemplate = rt; } /** * 存储 * * @param cacheKey 缓存key * @param value 值 * @param timeout 超时时间 秒 */ public static void save(String cacheKey, Object value, long timeout) { redisTemplate.opsForValue().set(cacheKey, value, timeout, TimeUnit.SECONDS); } public static void save(String cacheKey, Object value) { save(cacheKey, value, -1); } /** * 获取 * * @param cacheKey 缓存key * @param c 返回值类型 * @return c类型的对象 */ public static <T> T get(String cacheKey, Class<T> c) { Object o = redisTemplate.opsForValue().get(cacheKey); return JSON.parseObject(JSON.toJSONString(o), c); } /** * 缓存是否存在 * * @param cacheKey 缓存key * @return true 存在 */ public static boolean hasKey(String cacheKey) { return Boolean.TRUE.equals(redisTemplate.hasKey(cacheKey)); } /** * 添加到set集合 * * @param cacheKey 缓存key * @param value 值 一个或多个 */ public static void addForSet(String cacheKey, Object... value) { redisTemplate.opsForSet().add(cacheKey, value); } /** * 获取set集合 * * @param cacheKey 缓存 * @param c 返回值类型 * @return c类型的set集合 */ public static <T> Set<T> getForSet(String cacheKey, Class<T> c) { Set<Object> set = redisTemplate.boundSetOps(cacheKey).members(); if (Objects.isNull(set)) { return null; } return set.stream().map(o -> JSON.parseObject(JSON.toJSONString(o), c)).collect(Collectors.toSet()); } /** * 设置过期时间 * * @param cacheKey 缓存 key * @param timeout 过期时间(秒) */ public static void expire(String cacheKey, long timeout) { redisTemplate.expire(cacheKey, timeout, TimeUnit.MINUTES); } /** * 删除指定缓存 * * @param cacheKey 缓存key */ public static Boolean delete(String cacheKey) { return redisTemplate.delete(cacheKey); } /** * 指定元素删除 * * @param cacheKey 缓存 * @param objKey 集合元素 */ public static void remove(String cacheKey,String objKey) { redisTemplate.boundSetOps(cacheKey).remove(objKey); }}
3. 编写实体,并实现序列化接口
User.java
@Data@Accessors(chain = true)public class User implements Serializable { private static final long serialVersionUID = 1L; /** * 用户id */ private Long userId; /** * 用户名 */ private String username; /** * 性别 */ private String sex; /** * 备注 */ private String remark;}
注意:实体一定要加上序列化
4. 测试手动存储
- 编写测试接口
@RestController@RequiredArgsConstructorpublic class IndexController { /** * 手动设置缓存 */ @GetMapping("/set") public String set() { RedisUtils.save("key", new User().setUserId(999L).setSex("男").setUsername("王小锤"), 6000); return "设置成功 !"; } /** * 手动获取缓存 */ @GetMapping("/get") public User get() { return RedisUtils.get("key", User.class); }}
-
启动项目浏览器输入
ip+端口+/set
添加缓存
-
ip+端口+/get
获取缓存
5. 注解方式实现
- 在前面配置
RedisConfig.java
中加入如下配置
/** * 此处重写·springboot注解方式·缓存的Key生成规则 * key=包+方法名+参数 */ @Override @Bean public KeyGenerator keyGenerator() { return (target, method, params) -> { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append(method.getName()); for (Object obj : params) { sb.append(Objects.nonNull(obj) ? obj.toString() : "null"); } System.out.println(sb); return sb.toString(); }; } /** * 此处重写·springboot注解方式·缓存的序列化方式及有效时间 * key=包+方法名+参数 */ @Bean public CacheManager cacheManager(RedisConnectionFactory connectionFactory) { RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() // 将::改写为一个: .computePrefixWith(cacheName -> cacheName + ":") // 设置缓存有效期24小时 .entryTtl(Duration.ofHours(24)) // 禁止空值 .disableCachingNullValues() // 设置序列化规则 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); return RedisCacheManager.builder(connectionFactory) .cacheDefaults(cacheConfiguration) .build(); }
- 编写service 接口层
IUserService.java
public interface IUserService { /** * 缓存测试 * * @param id 用户id 随便输入 * @return 用户数据 */ User getUser(Long id); /** * 删除缓存测试 * * @param id 用户id 随便输入 */ void delUser(Long id);}
- 编写实现类
UserServiceImpl.java
,模拟数据库存储 - 在我们需要用缓存的方法上添加
@Cacheable(value = "user-key")
- 需要刷新缓存的地方添加
@CacheEvict(value = "user-key", allEntries = true)
@Service@Slf4jpublic class UserServiceImpl implements IUserService { @Override @Cacheable(value = "user-key") public User getUser(Long id) { return this.getUserInfo(id); } @Override @CacheEvict(value = "user-key", allEntries = true) public void delUser(Long id) { this.delUserInfo(id); } public void delUserInfo(Long id) { log.info("删除{}用户数据执行了!!", id); } /** * 模拟数据库查询 */ private User getUserInfo(Long id) { log.info("获取用户数据执行了!!"); return new User().setUserId(id).setUsername("王小锤").setSex("男").setRemark("注解方式aop实现缓存"); }}
- 效果
观察控制台我们可以发现,当我们查询的id一样时,只有第一次会触发getUserInfo方法,之后就会走缓存进行获取,只有当id不一样时,才会从新去调用getUserInfo方法。
6. 源码分享
本项目已收录
- Springboot-cli开发脚手架,集合各种常用框架使用案例,完善的文档,致力于让开发者快速搭建基础环境并让应用跑起来,并提供丰富的使用示例供使用者参考,快速上手。
- 项目源码github地址
- 项目源码国内gitee地址
创作打卡挑战赛 赢取流量/现金/CSDN周边激励大奖