我们使用redis
的时候,一般都是以下这个步骤
redis
缓存
redis
缓存)
redis
缓存)
此外,如果是增删改操作,将触发一次设置redis
缓存的操作。
上面的一些步骤高度重复,我决定造个轮子,基于注解、切面和反射来完成此项功能。
处于SpringBoot
中,redis
、aop
等相关依赖不要忘记
<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>
首先,我们先编写一个注解
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;
/**
* 生成key的模式策略
*/
enum RedisCacheKeyModel {
/**
* 自定义的key
*/
CUSTOM(),
/**
* 方法名_hash值
*/
METHOD_HASH();
}
}
其次,我们编写切面了,针对上面注解的方法
简单的来说,就是获取注解标注的方法,通过注解上的参数,来确定key
。
有了key
,先查询一遍redis
,如果有值就直接返回;异步调用方法,并更新redis缓存;
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;
}
}
}
写一段测试方法,进行使用
TestController.java
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
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缓存 |
---|---|
https://banmoon-pic.oss-cn-guangzhou.aliyuncs.com/images/20220901154509.png https://banmoon-pic.oss-cn-guangzhou.aliyuncs.com/images/20220901154417.png
注解、aop
和反射配在一起可以做很多事!
许多轮子不就这样出来了么。
我是半月,祝你幸福!!!