Implementing Interface Rate Limiting with Redis, Custom Annotations, and AOP in Spring
This article explains how to implement interface rate limiting in a Spring application by creating custom annotations, using AOP for method interception, and leveraging a Redis‑like ConcurrentMap cache to control request counts within a fixed time window.
Interface throttling is a common requirement to prevent a single micro‑service from being overwhelmed by excessive concurrent requests, ensuring smooth user experience by limiting the number of accesses to an endpoint within a fixed time period.
1. Custom Annotation
Annotations in Java are defined with @Target, @Retention, and optionally @Repeatable. The @Target specifies where the annotation can be applied (e.g., METHOD, PARAMETER), @Retention defines its lifecycle (RUNTIME, CLASS, SOURCE), and @Repeatable indicates whether it can be used multiple times on the same element.
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable
public @interface 注解名{
类型 属性名() default 默认值,
...
}2. AOP Overview
To use Aspect‑Oriented Programming (AOP) in Spring, add the AspectJ dependency and import the relevant annotations such as @Aspect, @Component, @Pointcut, @Before, etc. The Aspect annotation marks a class as a cross‑cutting concern, where you can define pointcuts and advice (before, after, around).
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>3. Example: Simulated Redis Rate Limiting
First, define a custom annotation @InCache that carries a key and a default value representing the allowed number of accesses.
// ElementType.FIELD for fields, ElementType.PARAMETER for method parameters, ElementType.METHOD for methods
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface InCache {
String key() default "";
int value() default 0;
}Next, create a controller with a login endpoint that uses the annotation.
@RestController
@CrossOrigin
public class UserController {
@Resource
UserServiceImpl userService;
@GetMapping("login")
@ResponseBody
@InCache(key = "login", value = 3)
public String login() {
Integer value = AopUtil.redis.get("login");
if (value == 0)
return "访问次数刷完了,访问被refuse";
return "第" + (4 - value) + "次访问:" + userService.login("account", "password");
}
}
@Component
public class UserServiceImpl {
public String login(String account, String password) {
if (account.equals("account") && password.equals("password")) {
System.out.println("----------user login success -------");
return "success";
} else {
return "fail";
}
}
}The AOP utility class defines a pointcut for the login method and a @Before advice that checks the @InCache annotation, manipulates a ConcurrentMap (used here to simulate Redis), and decrements the remaining count.
@Aspect
@Component
@Slf4j
public class AopUtil {
// Simulated Redis cache
public static ConcurrentMap<String, Integer> redis = new ConcurrentHashMap<>();
@Pointcut("execution(public String com.example.aop.UserController.login())")
public void interPoint() {}
@Before("interPoint()")
public void checkAnnotation(JoinPoint joinPoint) {
Object[] params = joinPoint.getArgs();
if (params.length != 0)
System.out.println("切入方法的参数列表为:" + Arrays.toString(params));
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
InCache annotation = method.getAnnotation(InCache.class);
if (annotation == null) {
System.out.println("该方法没有使用InCache注解!");
return;
}
String key = annotation.key();
if (redis.containsKey(key)) {
log.info("缓存已存在此key,将value进行减一操作");
Integer oldValue = redis.get(key);
if (oldValue > 0) {
redis.remove(key);
redis.put(key, oldValue - 1);
} else {
log.info("缓存中的此key已经为0,拒绝接口访问数据库!");
}
} else {
log.info("将key 与 value 存入缓存");
redis.put(key, annotation.value());
}
}
}Running the application shows that the login endpoint can only be accessed the configured number of times before further requests are rejected, effectively achieving basic rate limiting.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
