使用SpringBoot Aop机制实现接口数据缓存

使用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(), "");
        }
    }

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注