使用SpringBoot Aop机制实现接口数据缓存
在服务器接口的实际运行中 获取数据的接口的使用频率如果每次都请求数据库 那么将对服务器的资源造成浪费.
例如: 获取首页的公告接口 每个人访问一次都需要请求一次数据库 那么如果成千上万次访问 那么程序将执行成千上万次无意义的运行 这时我们需要将之前读取的数据存入内存中或者缓存中 在下次访问时直接拿取缓存中的数据即可
String cache = RedisUtilsEx.get("examine-member-cache-"+memberId);//从缓存中获取数据
if(StringUtils.isNotBlank(cache)){//如果数据不为空,则直接返回数据
return JSONUtilsEx.deserialize(cache,Result.class);//json转对象
}
Result res = adminService.examineMember(Integer.valueOf(memberId));//否则调用业务层请求数据库获取数据
RedisUtilsEx.set("examine-member-cache-"+memberId, JSONUtilsEx.serialize(res),3);数据转json后存入缓存
return res;
那么,如果每个接口都要如此做,也是烦不胜烦, 那么有没有办法让我们能一劳永逸的完成这个功能呢?
我决定使用aop+注解来实现 下面贴代码:
package com.ls.reg.config;
import com.ls.reg.bin.base.Result;
import com.ls.reg.utils.JSONUtilsEx;
import com.ls.reg.utils.RedisUtilsEx;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class CacheRedisAspect {
/**
* 此处的切点是注解的方式,也可以用包名的方式达到相同的效果
* '@Pointcut("execution(* yxm.zyf.love.service.impl.*.*(..))")'
*/
@Pointcut("@annotation(CacheRedis)")
public void cachePoint(){}
/**
* 环绕通知:
* 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
* 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
* */
@Around("cachePoint() && @annotation(cacheRedis)")
public Result doAround(ProceedingJoinPoint joinPoint, CacheRedis cacheRedis) throws Throwable{
String methodName = joinPoint.getSignature().getName();//方法名
String redisKey = methodName+"_";
Object[] paramValues = joinPoint.getArgs();
String[] paramNames = ((CodeSignature)joinPoint.getSignature()).getParameterNames();
for (int i = 0; i < paramNames.length; i++) {
redisKey = redisKey+paramNames[i]+"-"+paramValues[i]+"_";
}
String cacheValue = RedisUtilsEx.get(redisKey);
Result result;
if(cacheValue==null){
result = (Result) joinPoint.proceed();//调用执行目标方法
RedisUtilsEx.set(redisKey,JSONUtilsEx.serialize(result),cacheRedis.seconds());
System.out.println("接口"+methodName+"未使用缓存");
}else{
result = JSONUtilsEx.deserialize(cacheValue,Result.class);
System.out.println("接口"+methodName+"使用了redis");
}
return result;
}
/**
* 进入业务方法前
* */
@Before("cachePoint()")
public void doBeforeAdvice(JoinPoint joinPoint){
// System.out.println("进入方法前执行.....");
}
/**
* 处理完请求,返回内容
* @param ret
*/
@AfterReturning(returning = "ret", pointcut = "cachePoint()")
public void doAfterReturning(Object ret) {
// System.out.println("方法的返回值 : " + ret);
}
/**
* 后置异常通知
*/
@AfterThrowing("cachePoint()")
public void throwss(JoinPoint jp){
// System.out.println("方法异常时执行.....");
}
/**
* 后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
*/
@After("cachePoint()")
public void after(JoinPoint jp){
// System.out.println("方法最后执行.....");
}
}
package com.ls.reg.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CacheRedis {
int seconds()default 1;
}
这样在需要增加数据缓存的接口方法上增加一个@CachRedis注解就可以实现缓存 此功能是以 “方法名_参数名-参数值_参数名-参数值_”的key来存入缓存的 可以应对不同的入参缓存 配合用户登录token令牌认证可实现用户缓存的独立性
@PostMapping(value="/getWarPlacePage")
@CacheRedis(seconds = 10)//缓存10秒 不填默认为1秒
public Result getWarPlacePage(
String pageNum,
String pageSize
) {
try {
return adminService.getWarPlacePage(Integer.valueOf(pageNum),Integer.valueOf(pageSize));
} catch (Exception e) {
e.printStackTrace();
return new Result(Result.ERROR, e.getMessage(), "");
}
}