Backend Development 8 min read

Customizing and Using Interceptors in Spring Boot to Prevent Duplicate Submissions

This article explains how to create, configure, and apply custom Spring MVC interceptors in Spring Boot, including a practical example that uses Redis to block repeated requests within a short time window, with full code demonstrations.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Customizing and Using Interceptors in Spring Boot to Prevent Duplicate Submissions

Introduction: The previous article covered basic Spring Boot web development; this article introduces how to customize and configure interceptors in Spring Boot.

Spring Boot version: The examples are based on Spring Boot 2.3.4.RELEASE .

What is an interceptor? In Spring MVC, an Interceptor is similar to a Servlet Filter and can be used for permission verification, logging, login checks, and other request‑preprocessing tasks.

How to create a custom interceptor? Implement the HandlerInterceptor interface, which defines three methods:

preHandle() : executed before the controller method; returning true continues processing, false aborts.

postHandle() : executed after the controller method but before view rendering.

afterCompletion() : executed after the entire request is completed, useful for resource cleanup.

How to enable the interceptor in Spring Boot? Define a configuration class that implements WebMvcConfigurer and override addInterceptors() to register the interceptor.

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private XXX xxx;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // URIs that should not be intercepted
        final String[] commonExclude = {};
        registry.addInterceptor(xxx).excludePathPatterns(commonExclude);
    }
}

Example scenario: Prevent rapid duplicate requests (e.g., caused by repeated clicks) within a short time window using an interceptor.

Idea: Before the controller method executes, check whether the same request has been made within a specified period (e.g., 5 seconds). If it has, reject the request; otherwise, allow it.

Determining the unique key can involve IP address, user identifier, request parameters, request URI, or any combination of these.

Because the data needs to expire quickly, Redis is a suitable storage medium.

Implementation steps:

Create a custom annotation @RepeatSubmit with a seconds attribute (default 5 seconds). @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface RepeatSubmit { /** Default expiration time of 5 seconds */ long seconds() default 5; }

Create a RepeatSubmitInterceptor component that checks for the annotation on methods or classes, uses StringRedisTemplate to set a key with setIfAbsent , and throws RepeatSubmitException if the key already exists. @Component public class RepeatSubmitInterceptor implements HandlerInterceptor { @Autowired private StringRedisTemplate stringRedisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod method = (HandlerMethod) handler; // Annotation on method RepeatSubmit repeatSubmitByMethod = AnnotationUtils.findAnnotation(method.getMethod(), RepeatSubmit.class); // Annotation on class RepeatSubmit repeatSubmitByCls = AnnotationUtils.findAnnotation(method.getMethod().getDeclaringClass(), RepeatSubmit.class); if (repeatSubmitByMethod == null && repeatSubmitByCls == null) { return true; // No restriction, continue } // Example: use request URI as the key (in real projects combine more criteria) String uri = request.getRequestURI(); long timeout = (repeatSubmitByMethod != null) ? repeatSubmitByMethod.seconds() : repeatSubmitByCls.seconds(); Boolean ifAbsent = stringRedisTemplate.opsForValue() .setIfAbsent(uri, "", timeout, TimeUnit.SECONDS); // If the key already exists, reject the request if (ifAbsent != null && !ifAbsent) { throw new RepeatSubmitException(); } } return true; } }

Configure the interceptor in a WebConfig class that implements WebMvcConfigurer , injecting the interceptor and excluding certain paths. @Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private RepeatSubmitInterceptor repeatSubmitInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { // URIs that should not be intercepted final String[] commonExclude = {"/error", "/files/**"}; registry.addInterceptor(repeatSubmitInterceptor).excludePathPatterns(commonExclude); } }

Apply the @RepeatSubmit annotation to a controller or specific methods to activate the duplicate‑submission protection.

@RestController
@RequestMapping("/user")
@RepeatSubmit // All endpoints in this controller are protected
public class LoginController {
    @RequestMapping("/login")
    public String login() {
        return "login success";
    }
}

Note: When both class‑level and method‑level annotations are present, the method‑level seconds value overrides the class‑level value.

Conclusion: Configuring an interceptor in Spring Boot is straightforward and provides an effective way to prevent duplicate submissions.

JavaRedisSpring Bootweb developmentInterceptorduplicate submission
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.