spring AOP + redis实现一个url拦截器,防止重复提交

为了防止重复提交请求造成的问题,这里我们使用aop加上redis缓存做一个拦截器。为了方便使用,我们以注解的形式来使用。

一、新建一个自定义注解

import java.lang.annotation.*;

/**
 * 针对Controller的Action拦截,
 * 关键字是RequestMapping第一个value,
 * 如无value将不进行拦截
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface UrlInterceptFlag {
    /**
     *默认过期时间是1000毫秒,在1000毫秒内只能操作一次
     * @return
     */
    long millisecond() default 1000;
}
上面的注解只有一个参数,就是设置过期时间,同一个url在设置时间内只会被调用一次。防止重复操作。然后我们利用spring aop对使用了UrlInterceptFlag注解的方法进行拦截,是用redis进行缓存我们的@RequestMapping的value,缓存时间就是我们的注解时间。来看具体的代码:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.PostConstruct;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;

@Aspect//切面编程注解
@Order(-1)//定义优先级,value越小 优先级越高
@Component
public class UrlInterceptHandleAspect {
    private Logger logger = LoggerFactory.getLogger(UrlInterceptHandleAspect.class);

    private final static String CACHE_PREFIX = "AOP:STUDY:";

   //自己实现的redis工具类。
    @Autowired
    private RedisClient redisClient;


    @PostConstruct
    public void init() {
        logger.info("Url拦截切面初始化");
    }

    /**
     * 对Controller的Action操作在某时间段内进行限制操作次数
     *
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(value = "@annotation(com.datatrees.loan.clearing.controller.UrlIntercept.UrlInterceptFlag)")
    //上面value里面的值就是我们url拦截注解的包路径
    public Object intercept(ProceedingJoinPoint pjp) throws Throwable {
        Object obj = null;
        //获取到类的RequestMapping
        RequestMapping classRequestMapping = pjp.getTarget().getClass().getAnnotation(RequestMapping.class);
        //获取目标方法签名
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        //获取到方法
        Method method = signature.getMethod();
        //获取到方法上的urlInterceptFlag注解
        UrlInterceptFlag urlInterceptFlag = method.getAnnotation(UrlInterceptFlag.class);
        //获取方法上的RequestMapping
        RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
        //类上RequestMapping的value值
        String classKeys[] = classRequestMapping.value();
        //方法上RequestMapping的value值
        String methodKeys[] = methodRequestMapping.value();
        if (methodKeys.length > 0) {
            String classKey = "";
            if (classKeys.length > 0) {
                classKey = classKeys[0];
            }
            //然后以我们的CACHE_PREFIX+类和方法的RequestMapping的value值(也就是我们的请求路径后缀,确定唯一)作为key存入redis,值随便存点什么,这里存了个1,这是redis key的有效时间为我们urlInterceptFlag注解设置的时间。
            if (redisClient.setIfAbsent(CACHE_PREFIX + classKey + methodKeys[0], 1, urlInterceptFlag.millisecond())) {
            //redisClient.setIfAbsent返回一个boolean值,如果缓存设置成功(也就是之前对应的缓存不存在),我们就进行后续操作。调用pjp.proceed()
                logger.info("method={},value={}", method.getName(), methodKeys[0]);
                obj = pjp.proceed();
            } else {
            //这里表示缓存存在,那么我们就返回一个方法的新实例。
                obj = method.getReturnType().getConstructor().newInstance();
                logger.info("method={},value={}频繁操作,请稍后再试", method.getName(), methodKeys[0]);
            }
        }
        return obj;
    }
 

通过上面的方法,使用了urlInterceptFlag注解的方法都会被我们的切面拦截到,然后请求路径(类和方法上的RequestMapping注解的value)通过我们的规则组装成key去redis查询,如果key存在(缓存还没过期)我们就返回一个新的实例,如果不存在,我们就可以继续操作。

你可能感兴趣的:(学习笔记,问题解析)