SpringBoot中使用注解读取redis缓存
一、介绍
我们使用redis
的时候,一般都是以下这个步骤
-
查询指定的redis
缓存
-
如果有直接返回,(异步执行查询,更新redis
缓存)
-
如果没有则执行查询,(同时设置redis
缓存)
此外,如果是增删改操作,将触发一次设置redis
缓存的操作。
上面的一些步骤高度重复,我决定造个轮子,基于注解、切面和反射来完成此项功能。
二、相关代码
1)依赖
处于SpringBoot
中,redis
、aop
等相关依赖不要忘记
1 2 3 4 5 6 7 8 9 10 11 12
| <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
|
2)代码
首先,我们先编写一个注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package com.banmoon.test.annotations;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface RedisCache {
String value() default "hash";
RedisCacheKeyModel keyModel() default RedisCacheKeyModel.METHOD_HASH;
boolean write() default false;
enum RedisCacheKeyModel {
CUSTOM(),
METHOD_HASH();
}
}
|
其次,我们编写切面了,针对上面注解的方法
简单的来说,就是获取注解标注的方法,通过注解上的参数,来确定key
。
有了key
,先查询一遍redis
,如果有值就直接返回;异步调用方法,并更新redis缓存;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| package com.banmoon.test.aspect;
import com.banmoon.test.annotations.RedisCache; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component;
import java.lang.reflect.Method; import java.util.Objects; import java.util.concurrent.CompletableFuture;
@Slf4j @Aspect @Component public class RedisCacheAspect {
@Autowired private RedisTemplate redisTemplate;
@Pointcut("@annotation(com.banmoon.test.annotations.RedisCache)") public void pointcut() { }
@Around("pointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { String key = ""; try { String methodName = joinPoint.getSignature().getName(); Class<?> classTarget = joinPoint.getTarget().getClass(); Class<?>[] par = ((MethodSignature) joinPoint.getSignature()).getParameterTypes(); Method objMethod = classTarget.getMethod(methodName, par);
RedisCache cache = objMethod.getAnnotation(RedisCache.class); if (RedisCache.RedisCacheKeyModel.CUSTOM == cache.keyModel()) { key = cache.value(); } else { String canonicalName = String.format("%s.%s", classTarget.getCanonicalName(), methodName); key = String.format("%s_%s", methodName, canonicalName.hashCode()); }
if (cache.write()) { Object res = joinPoint.proceed(); redisTemplate.opsForValue().set(key, res); return res; } else { Object res = redisTemplate.opsForValue().get(key); if (Objects.isNull(res)) { res = joinPoint.proceed(); redisTemplate.opsForValue().set(key, res); } else { String finalKey = key; CompletableFuture.runAsync(() -> { try { Object res1 = joinPoint.proceed(); redisTemplate.opsForValue().set(finalKey, res1); } catch (Throwable throwable) { } }); } return res; } } catch (Exception e) { Object res = joinPoint.proceed(); redisTemplate.opsForValue().set(key, res); return res; } }
}
|
3)测试使用
写一段测试方法,进行使用
TestController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package com.banmoon.test.controller;
import com.banmoon.test.dto.ResultData; import com.banmoon.test.dto.UserDTO; import com.banmoon.test.service.impl.TestServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController @RequestMapping("/test") public class TestController {
@Autowired private TestServiceImpl testServiceImpl;
@GetMapping("/redisCache") public ResultData redisCache() { List<UserDTO> userList = testServiceImpl.getUserList(); return ResultData.success(userList); }
}
|
TestServiceImpl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package com.banmoon.test.service.impl;
import com.banmoon.test.annotations.RedisCache; import com.banmoon.test.dto.UserDTO; import org.springframework.stereotype.Service;
import java.util.ArrayList; import java.util.List;
@Service public class TestServiceImpl {
@RedisCache("userList") public List<UserDTO> getUserList() { List<UserDTO> list = new ArrayList<>(); for (int i = 0; i < 5; i++) { UserDTO dto = new UserDTO(); dto.setName("半月无霜" + i); dto.setSex("男"); dto.setAge(18+i); dto.setStatus(i%2); list.add(dto); } return list; }
}
|
请求url,http://localhost:8080/test/redisCache,可以正常返回,同时查看`redis`有无缓存
请求结果 |
redis缓存 |
|
|
三、最后
注解、aop
和反射配在一起可以做很多事!
许多轮子不就这样出来了么。
我是半月,祝你幸福!!!