兄弟们,相信遇到过重复请求的痛点吧,我也遇过,因此,写了一个自定义注解去解决这个问题,接下来看代码。
首先:创建一个自定义注解 RequestLimit 。然后字段的话是 second、maxCount。看代码
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description 请求限流/防刷自定义注解
* @date 2022/04/05 18:20
* @author by TheSky
*/
@Documented
@Inherited
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestLimit {
// 在 second 秒内,最大只能请求 maxCount 次
int second() default 1;
int maxCount() default 1;
}
接下来就是拦截器了,创建拦截器: RequestLimitIntercept 继承 HandlerInterceptorAdapter
看代码:
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
/**
* @Description 请求限流/防刷拦截器
* @date 2022/04/05 18:41
* @author by TheSky
*/
@Slf4j
@Component
public class RequestLimitIntercept extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/**
* isAssignableFrom() 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口
* isAssignableFrom()方法是判断是否为某个类的父类
* instanceof关键字是判断是否某个类的子类
*/
if (handler.getClass().isAssignableFrom(HandlerMethod.class)) {
//HandlerMethod 封装方法定义相关的信息,如类,方法,参数等
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 获取方法中是否包含注解
RequestLimit methodAnnotation = method.getAnnotation(RequestLimit.class);
//获取 类中是否包含注解,也就是controller 是否有注解
RequestLimit classAnnotation = method.getDeclaringClass().getAnnotation(RequestLimit.class);
// 如果 方法上有注解就优先选择方法上的参数,否则类上的参数
RequestLimit requestLimit = methodAnnotation != null ? methodAnnotation : classAnnotation;
if (requestLimit != null) {
if (isLimit(request, requestLimit)) {
resonseOut(response, new MyResult().faild("请求频繁,请稍后再试!"));
return false;
}
}
}
return super.preHandle(request, response, handler);
}
//判断请求是否受限
public boolean isLimit(HttpServletRequest request, RequestLimit requestLimit) {
// 受限的redis 缓存key ,因为这里用浏览器做测试,我就用sessionid 来做唯一key,如果是app ,可以使用 用户ID 之类的唯一标识。
String UUID = request.getHeader("UUID");
if (StringUtils.isBlank(UUID)) {
log.warn("缺少唯一标识!");
return false;
}
String limitKey = request.getServletPath() + UUID;
// 从缓存中获取,当前这个请求访问了几次
String redisCount = (String) RedisUtil.get(limitKey);
if (redisCount == null || StringUtils.equalsIgnoreCase(redisCount, "null")) {
//初始次数及过期时间;将值设置进redis,过期时间的话就是上面的自定义注解的秒数
RedisUtil.set(limitKey, "1", requestLimit.second(), TimeUnit.SECONDS);
} else {
Integer count = Integer.valueOf(redisCount);
if (count >= requestLimit.maxCount()) {
return true;
}
// 次数自增
RedisUtil.increment(limitKey);
}
return false;
}
/**
* 回写给客户端
*
* @param response
* @param result
* @throws IOException
*/
private void resonseOut(HttpServletResponse response, MyResult result) throws IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null;
String json = JSONObject.toJSON(result).toString();
out = response.getWriter();
out.append(json);
}
}
接下来呢,就是将这个拦截器给实现起来。
创建 WebConfig 实现 WebMvcConfigurer。看代码:
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Description 请求限流/防刷 实现
* @date 2022/04/05 19:30
* @author by TheSky
*/
@Configuration
@Slf4j
public class WebConfig implements WebMvcConfigurer {
@Autowired
private RequestLimitIntercept requestLimitIntercept;
@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info("添加拦截");
registry.addInterceptor(requestLimitIntercept);
}
}
至此该注解 @RequestLimit 就可以被使用起来啦!
举个荔枝吧:
//在Controller的接口中就能使用啦
@GetMapping("/test")
@RequestLimit(maxCount = 1, second = 5)
public MyResult test(String t1, String t2) {
return new MyResult().ok();
}
至此,功能结束!有什么问题请多指教哈!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)