1.首先,需要在pom.xml文件中添加以下依赖,以使用Spring AOP和Ehcache缓存
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.6</version>
</dependency>
2.创建一个自定义注解@ApiLimit,用于标记需要限制访问次数的接口
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
int limit() default 10;
long interval() default 1000;
}
3.创建一个切面类RateLimitAspect,用于实现限制访问次数的逻辑。该切面类使用@Aspect和@Component注解进行标记,表示它是一个切面并且可以被Spring容器管理。
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class RateLimitAspect {
private Map<String, Long> requestCounts = new HashMap<>();
@Before("@annotation(rateLimit)")
public void checkRateLimit(JoinPoint joinPoint, RateLimit rateLimit) throws InterruptedException {
String methodName = joinPoint.getSignature().toShortString();
long limit = rateLimit.limit();
long interval = rateLimit.interval();
synchronized (requestCounts) {
long currentTime = System.currentTimeMillis();
if (requestCounts.containsKey(methodName)) {
long lastRequestTime = requestCounts.get(methodName);
long elapsedTime = currentTime - lastRequestTime;
if (elapsedTime < interval) {
if (requestCounts.getOrDefault(methodName, 0L) >= limit) {
TimeUnit.MILLISECONDS.sleep(interval - elapsedTime);
requestCounts.put(methodName, currentTime);
return;
}
}
}
requestCounts.put(methodName, currentTime);
}
}
}
在RateLimitAspect类中,我们使用@Before注解来指定在目标方法执行之前执行切面逻辑。通过@annotation(rateLimit)指定了切点,它表示切入带有RateLimit注解的方法。
在checkRateLimit方法中,我们首先获取目标方法的名称,并从requestCounts中查找该方法的上次请求时间。如果上次请求时间存在且与当前时间间隔不超过指定的interval,则判断请求次数是否超过了限制的limit。如果超过了限制,则通过TimeUnit.MILLISECONDS.sleep(interval - elapsedTime)暂停当前线程,以保持请求速率在限制范围内。
最后,我们将当前请求的时间存储到requestCounts中,以便下一次请求时使用。
4.使用以上代码,只需要在需要限制访问次数的接口方法上添加@RateLimit注解。
@RestController
public class MyController {
@GetMapping("/myApi")
@RateLimit(limit = 5, interval = 1000) // 每秒最多允许5次访问
public String myApi() {
// 处理接口逻辑
return "Hello World!";
}
}