Implementing Rate Limiting in Spring Boot Using Custom Annotations and Redis
This article demonstrates how to create a custom @AccessLimit annotation in Spring Boot, implement a rate‑limiting interceptor using Redis, register the interceptor, and apply the annotation to controller methods to restrict request frequency, with full Java code examples.
This guide explains how to add request rate limiting to a Spring Boot application by defining a custom @AccessLimit annotation, implementing an interceptor that checks Redis for request counts, registering the interceptor, and using the annotation on controller endpoints.
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {
int seconds();
int maxCount();
boolean needLogin() default true;
}The interceptor retrieves the annotation from the handler method, extracts the limit parameters, builds a Redis key (optionally including a user identifier when login is required), and checks the request count stored in Redis.
import com.alibaba.fastjson.JSON;
import com.example.demo.action.AccessLimit;
import com.example.demo.redis.RedisService;
import com.example.demo.result.CodeMsg;
import com.example.demo.result.Result;
import org.springframework.beans.factory.annotation.Autowired;
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.OutputStream;
@Component
public class FangshuaInterceptor extends HandlerInterceptorAdapter {
@Autowired
private RedisService redisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
if (accessLimit == null) {
return true;
}
int seconds = accessLimit.seconds();
int maxCount = accessLimit.maxCount();
boolean login = accessLimit.needLogin();
String key = request.getRequestURI();
if (login) {
// Assume user ID is 1 for demonstration; replace with actual user ID retrieval
key += "" + "1";
}
AccessKey ak = AccessKey.withExpire(seconds);
Integer count = redisService.get(ak, key, Integer.class);
if (count == null) {
redisService.set(ak, key, 1);
} else if (count < maxCount) {
redisService.incr(ak, key);
} else {
render(response, CodeMsg.ACCESS_LIMIT_REACHED);
return false;
}
}
return true;
}
private void render(HttpServletResponse response, CodeMsg cm) throws Exception {
response.setContentType("application/json;charset=UTF-8");
OutputStream out = response.getOutputStream();
String str = JSON.toJSONString(Result.error(cm));
out.write(str.getBytes("UTF-8"));
out.flush();
out.close();
}
}Register the interceptor in a Spring configuration class so that it becomes part of the MVC interceptor chain.
import com.example.demo.ExceptionHander.FangshuaInterceptor;
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.WebMvcConfigurerAdapter;
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
private FangshuaInterceptor interceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor);
}
}Finally, apply the @AccessLimit annotation to a controller method to enforce the defined limits.
import com.example.demo.result.Result;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class FangshuaController {
@AccessLimit(seconds = 5, maxCount = 5, needLogin = true)
@RequestMapping("/fangshua")
@ResponseBody
public Result
fangshua() {
return Result.success("请求成功");
}
}By following these steps, developers can protect their APIs from excessive calls, ensuring stability and preventing abuse.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
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.