DebounceAspect.java 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. package com.izouma.nineth.aspect;
  2. import com.izouma.nineth.annotations.Debounce;
  3. import com.izouma.nineth.aspect.debounce.DebounceTask;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.aspectj.lang.ProceedingJoinPoint;
  6. import org.aspectj.lang.annotation.Around;
  7. import org.aspectj.lang.annotation.Aspect;
  8. import org.aspectj.lang.annotation.Pointcut;
  9. import org.aspectj.lang.reflect.MethodSignature;
  10. import org.springframework.core.DefaultParameterNameDiscoverer;
  11. import org.springframework.expression.EvaluationContext;
  12. import org.springframework.expression.ExpressionParser;
  13. import org.springframework.expression.spel.standard.SpelExpressionParser;
  14. import org.springframework.expression.spel.support.StandardEvaluationContext;
  15. import org.springframework.stereotype.Component;
  16. import java.lang.reflect.Method;
  17. import java.util.HashMap;
  18. import java.util.Map;
  19. import java.util.Optional;
  20. import java.util.concurrent.Executors;
  21. import java.util.concurrent.Future;
  22. import java.util.concurrent.ScheduledExecutorService;
  23. import java.util.concurrent.TimeUnit;
  24. @Aspect
  25. @Component
  26. @Slf4j
  27. public class DebounceAspect {
  28. private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
  29. private HashMap<String, Future<Void>> debounceStore = new HashMap<>();
  30. private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
  31. private Map<String, Long> debounceCounter = new HashMap<>();
  32. @Pointcut("@annotation(com.izouma.nineth.annotations.Debounce)")
  33. public void debouncePointCut() {
  34. }
  35. @Around(value = "debouncePointCut() && @annotation(debounce)")
  36. public synchronized void debounce(ProceedingJoinPoint joinPoint, Debounce debounce) {
  37. ExpressionParser parser = new SpelExpressionParser();
  38. EvaluationContext context = new StandardEvaluationContext(joinPoint.getSignature());
  39. MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
  40. Method method = methodSignature.getMethod();
  41. String[] paramNames = nameDiscoverer.getParameterNames(method);
  42. Object[] args = joinPoint.getArgs();
  43. for (int i = 0; i < args.length; i++) {
  44. context.setVariable(paramNames[i], args[i]);
  45. }
  46. String key = Optional.ofNullable(parser.parseExpression(debounce.key()).getValue(context)).map(Object::toString)
  47. .orElse("default");
  48. Future<Void> future = debounceStore.get(key);
  49. long lastRun = debounceCounter.getOrDefault(key, 0L);
  50. if (future != null && !future.isDone()) {
  51. if (System.currentTimeMillis() - lastRun > debounce.delay()) {
  52. debounceCounter.put(key, System.currentTimeMillis());
  53. } else {
  54. future.cancel(false);
  55. }
  56. }
  57. debounceStore.put(key, executorService.schedule(new DebounceTask(joinPoint, (Void) -> {
  58. debounceCounter.put(key, System.currentTimeMillis());
  59. return null;
  60. }), debounce.delay(), TimeUnit.MILLISECONDS));
  61. }
  62. }