为了防止重复提交请求造成的问题,这里我们使用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存在(缓存还没过期)我们就返回一个新的实例,如果不存在,我们就可以继续操作。