防抖
This commit is contained in:
@@ -5,6 +5,7 @@ import com.sqx.common.utils.DateUtils;
|
||||
import com.sqx.common.utils.HttpContextUtils;
|
||||
import com.sqx.common.utils.IPUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
@@ -32,7 +33,6 @@ public class AppApiMethodAspect {
|
||||
}
|
||||
|
||||
@Around("pkg()")
|
||||
// @SuppressWarnings("unchecked")
|
||||
public Object around(ProceedingJoinPoint pjp) throws Throwable {
|
||||
long start = System.currentTimeMillis();
|
||||
// 执行被拦截的方法
|
||||
@@ -41,15 +41,16 @@ public class AppApiMethodAspect {
|
||||
Object[] args = pjp.getArgs();
|
||||
String params = new Gson().toJson(args);
|
||||
String resultJson = new Gson().toJson(result);
|
||||
//获取request
|
||||
|
||||
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
log.info("\n>>>>>> {} {}\n>>>>>> IP: {} \n>>>>>> execute time:{}\n>>>>>> Request: {}\n>>>>>> Response: {}",
|
||||
request.getMethod(), request.getRequestURL(), IPUtils.getIpAddr(request),end-start,
|
||||
params,
|
||||
resultJson
|
||||
);
|
||||
if(StringUtils.isNotBlank(resultJson) && !"null".equals(resultJson)){
|
||||
log.info("\n>>>>>> {} {}\n>>>>>> IP: {} \n>>>>>> execute time:{}ms \n>>>>>> Request: {}\n>>>>>> Response: {}",
|
||||
request.getMethod(), request.getRequestURL(), IPUtils.getIpAddr(request),end-start,
|
||||
params,
|
||||
resultJson
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
117
src/main/java/com/sqx/common/aspect/DebounceAspect.java
Normal file
117
src/main/java/com/sqx/common/aspect/DebounceAspect.java
Normal file
@@ -0,0 +1,117 @@
|
||||
package com.sqx.common.aspect;
|
||||
|
||||
import com.sqx.common.annotation.Debounce;
|
||||
import com.sqx.common.utils.SpelUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
|
||||
/**
|
||||
* 防抖切面
|
||||
* @author ww
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class DebounceAspect {
|
||||
|
||||
@Pointcut("@annotation(com.sqx.common.annotation.Debounce)")
|
||||
public void logPointCut() {
|
||||
|
||||
}
|
||||
|
||||
// 用于存储基于方法和入参情况的上次执行时间,结构为:方法签名 -> (入参值 -> 上次执行时间)
|
||||
private static final ConcurrentHashMap<String, ConcurrentHashMap<Object, Long>> executionTimeMap = new ConcurrentHashMap<>();
|
||||
private static final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
@Around("logPointCut()")
|
||||
public Object aroundDebounce(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
cleanExpiredRecords();
|
||||
String methodSignature = joinPoint.getSignature().toLongString();
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Debounce annotation = signature.getMethod().getAnnotation(Debounce.class);
|
||||
long interval = annotation.interval();
|
||||
TimeUnit timeUnit = annotation.timeUnit();
|
||||
String value = annotation.value();
|
||||
if (StringUtils.isBlank(value)) {
|
||||
// 没有指定入参表达式,按照整个方法进行防抖
|
||||
return debounceForWholeMethod(joinPoint, methodSignature, interval, timeUnit);
|
||||
}
|
||||
String[] split = value.split(",");
|
||||
StringBuilder values = new StringBuilder();
|
||||
for (String str : split) {
|
||||
values.append(SpelUtil.generateKeyBySpEL(str, joinPoint));
|
||||
}
|
||||
// 解析入参表达式,获取对应入参的值
|
||||
|
||||
if (StringUtils.isBlank(values.toString())) {
|
||||
// 如果解析失败或值为null,按照整个方法进行防抖
|
||||
return debounceForWholeMethod(joinPoint, methodSignature, interval, timeUnit);
|
||||
}
|
||||
|
||||
// 根据方法签名和入参值进行防抖判断
|
||||
return debounceForSpecificValue(joinPoint, methodSignature, interval, timeUnit, values.toString());
|
||||
}
|
||||
|
||||
private Object debounceForWholeMethod(ProceedingJoinPoint joinPoint, String methodSignature, long interval, TimeUnit timeUnit) throws Throwable {
|
||||
ConcurrentHashMap<Object, Long> methodExecutionTimeMap = executionTimeMap.computeIfAbsent(methodSignature, k -> new ConcurrentHashMap<>());
|
||||
long currentTime = System.currentTimeMillis();
|
||||
Long lastTime = methodExecutionTimeMap.get(methodSignature);
|
||||
if (lastTime == null) {
|
||||
// 如果不存在对应键值对,设置初始值,这里设置为一个表示很久之前的时间戳,比如0
|
||||
lastTime = 0L;
|
||||
}
|
||||
if (lastTime == null || currentTime - timeUnit.toMillis(interval) >= lastTime) {
|
||||
// 满足防抖间隔,更新上次执行时间,并执行目标方法
|
||||
methodExecutionTimeMap.put(methodSignature, currentTime);
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
// 在防抖间隔内,不执行目标方法,直接返回
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object debounceForSpecificValue(ProceedingJoinPoint joinPoint, String methodSignature, long interval, TimeUnit timeUnit, Object targetValue) throws Throwable {
|
||||
ConcurrentHashMap<Object, Long> methodExecutionTimeMap = executionTimeMap.computeIfAbsent(methodSignature, k -> new ConcurrentHashMap<>());
|
||||
long currentTime = System.currentTimeMillis();
|
||||
Long lastTime = methodExecutionTimeMap.get(targetValue);
|
||||
if (lastTime == null || currentTime - timeUnit.toMillis(interval) >= lastTime) {
|
||||
// 满足防抖间隔,更新上次执行时间,并执行目标方法
|
||||
methodExecutionTimeMap.put(targetValue, currentTime);
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
// 在防抖间隔内,不执行目标方法,直接返回
|
||||
return null;
|
||||
}
|
||||
|
||||
public void cleanExpiredRecords() {
|
||||
long expirationTime = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(10);
|
||||
lock.lock();
|
||||
try {
|
||||
for (Entry<String, ConcurrentHashMap<Object, Long>> outerEntry : executionTimeMap.entrySet()) {
|
||||
String methodSignature = outerEntry.getKey();
|
||||
ConcurrentHashMap<Object, Long> innerMap = outerEntry.getValue();
|
||||
ConcurrentHashMap<Object, Long> keysToRemove = new ConcurrentHashMap<>();
|
||||
for (Entry<Object, Long> innerEntry : innerMap.entrySet()) {
|
||||
if (innerEntry.getValue() < expirationTime) {
|
||||
keysToRemove.put(innerEntry.getKey(), innerEntry.getValue());
|
||||
}
|
||||
}
|
||||
innerMap.keySet().removeAll(keysToRemove.keySet());
|
||||
if (innerMap.isEmpty()) {
|
||||
executionTimeMap.remove(methodSignature);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user