Code explanation
@RepeatSubmit
- is a custom annotation that is often used to prevent repeated submissions from form.
- This annotation can be applied to the controller method to ensure that the same request will not be submitted multiple times within a certain period of time.
Here are some common parameters and usages:
- value: The name or description of the annotation.
- interval: The minimum interval between two requests (usually milliseconds).
- message: The prompt message returned when a duplicate submission is detected.
Sample code
Suppose there is a @RepeatSubmit annotation defined as follows:
@Target() @Retention() public @interface RepeatSubmit { String value() default ""; int interval() default 3000; // 3 seconds by default String message() default "Do not repeat submission"; }
Example of usage
Use @RepeatSubmit annotation in the controller method:
@RestController public class UserController { @PostMapping("/submitForm") @RepeatSubmit(interval = 5000, message = "Please wait 5 seconds before submitting") public ResponseEntity<String> submitForm(@RequestBody FormData formData) { // Process form submission logic return ("Form submission successful"); } }
Control flow diagram
The following is the control flow diagram annotated by @RepeatSubmit, showing how it works:
flowchart TD
A[Start] --> B[Receive Request]
B --> C{Check whether to submit repeatedly}
C -->|Yes| D[Return to repeated submission prompt information]
C -->|No| E[process the request]
E --> F[Return to successful response]
F --> G[end]
illustrate
- A: Start processing of the request.
- B: Received the client's request.
- C: Check whether the interval between the current request and the previous request is less than the set interval.
- D: If a duplicate submission is detected, return the prompt message (such as "Please wait 5 seconds before submitting").
- E: If no duplicate submission is detected, continue processing of the request.
- F: After the request is processed successfully, a successful response is returned.
- G: End the request processing process.
Design Patterns Used
@RepeatSubmit annotations usually combine AOP (sectional-oriented programming) and interceptor mode to prevent repeated submissions from form.
Design Pattern Analysis
AOP (System-oriented Programming):
- Purpose: Separate cross-cutting concerns (such as logging, transaction management, security, etc.) from business logic to improve the modularity and maintainability of the code.
- Implementation: Use Spring AOP or other AOP framework to intercept method calls through Aspects and execute additional logic (such as checking for duplicate commits).
Interceptor mode
- Purpose: Execute specific logic before or after the request reaches the target method.
- Implementation: In Spring, interceptors can be implemented through HandlerInterceptor or MethodInterceptor, intercept requests and perform preprocessing or postprocessing logic.
Definition annotation
@Target() @Retention() public @interface RepeatSubmit { String value() default ""; int interval() default 3000; // 3 seconds by default String message() default "Do not repeat submission"; }
Create a section
@Aspect @Component public class RepeatSubmitAspect { @Around("@annotation(repeatSubmit)") public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable { // Get the method signature MethodSignature signature = (MethodSignature) (); Method method = (); // Get the request context HttpServletRequest request = ((ServletRequestAttributes) ()).getRequest(); // Get annotation parameters int interval = (); String message = (); // Get the last requested timestamp from the session Long lastRequestTime = (Long) ().getAttribute(()); // Check whether to submit repeatedly if (lastRequestTime != null && () - lastRequestTime < interval) { throw new RuntimeException(message); } // Record the time stamp of the current request ().setAttribute((), ()); // Continue to execute the target method return (); } }
Use annotations in controller methods
@RestController public class UserController { @PostMapping("/submitForm") @RepeatSubmit(interval = 5000, message = "Please wait 5 seconds before submitting") public ResponseEntity<String> submitForm(@RequestBody FormData formData) { // Process form submission logic return ("Form submission successful"); } }
What to pay attention to using @RepeatSubmit
When using the @RepeatSubmit annotation to prevent repeated submissions, you need to pay attention to the following aspects:
1. Annotation parameter configuration
- interval: Set a reasonable interval. Too short intervals may cause users to frequently encounter prompts of repeated submissions, affecting the user experience; too long intervals may not effectively prevent rapid and continuous submissions.
- message: Provide clear prompt information to inform the user why the request was denied, help the user understand and take the correct action.
2. Concurrent processing
- Thread safety: In a high concurrency environment, ensure that the read and write timestamp operations are thread-safe.
- Timestamps can be stored using thread-safe data structures such as ConcurrentHashMap or AtomicLong.
- Distributed Environment: If the application is deployed on multiple servers, you need to consider how to share timestamp information in a distributed environment.
- Timestamps can be stored using distributed caches such as Redis.
3. User experience
- Front-end tips: Add anti-repeated submission mechanisms on the front-end page, such as disabling the submission button, displaying loading animations, etc., to reduce the possibility of user misoperation.
- Error handling: Provides a friendly error handling mechanism, when a duplicate submission is detected, returns clear error information and guides the user to try again or contact support.
4. Performance considerations
- Performance overhead: Anti-repeated submission checks will increase certain performance overhead, especially in high concurrency scenarios. Make sure these checks do not become a bottleneck in system performance.
- Caching policy: Use cache to store timestamp information, reduce frequent access to databases or sessions, and improve performance.
5. Security
- Session Management: Ensure the security of session management and prevent attacks such as session hijacking.
- Timestamp Verification: Verify the validity and legality of timestamps to prevent malicious users from tampering with timestamps.
6. Logging
- Logging: Records the timestamp and processing results of each request, which facilitates subsequent audits and problem investigations.
Sample code
The following is a more complete @RepeatSubmit annotation and sectional implementation, taking into account the above precautions:
Definition annotation
@Target() @Retention() public @interface RepeatSubmit { String value() default ""; int interval() default 3000; // 3 seconds by default String message() default "Do not repeat submission"; } // Create facets@Aspect @Component public class RepeatSubmitAspect { @Autowired private RedisTemplate<String, Long> redisTemplate; @Around("@annotation(repeatSubmit)") public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable { // Get the method signature MethodSignature signature = (MethodSignature) (); Method method = (); // Get the request context HttpServletRequest request = ((ServletRequestAttributes) ()).getRequest(); // Get annotation parameters int interval = (); String message = (); // Generate a unique request identifier String key = () + ":" + (); // Get the last requested timestamp from Redis Long lastRequestTime = ().get(key); // Check whether to submit repeatedly if (lastRequestTime != null && () - lastRequestTime < interval) { throw new RuntimeException(message); } // Record the time stamp of the current request ().set(key, (), interval, ); // Continue to execute the target method return (); } } // Use annotations in the controller method@RestController public class UserController { @PostMapping("/submitForm") @RepeatSubmit(interval = 5000, message = "Please wait 5 seconds before submitting") public ResponseEntity<String> submitForm(@RequestBody FormData formData) { // Process form submission logic return ("Form submission successful"); } }
Control flow diagram
The following is the control flow diagram annotated by @RepeatSubmit, showing how it works:
flowchart TD A[start] --> B[Receive request] B --> C[Generate a unique request identifier] C --> D[from Redis Get the last requested timestamp] D -->|Exist and not expired| E[Return to repeated submission prompt information] D -->|Does not exist or has expired| F[Record the current request time stamp] F --> G[Continue to execute the target method] G --> H[Return a successful response] H --> I[Finish]
illustrate
- A: Start processing of the request.
- B: Received the client's request.
- C: Generate a unique request identifier, usually including the method name and client IP address.
- D: Get the last requested timestamp from Redis.
- E: If it exists and has not expired, return the repeated submission prompt message.
- F: If it does not exist or has expired, record the timestamp of the current request.
- G: Continue to execute the target method.
- H: After the request is processed successfully, a successful response is returned.
- I: End the request processing process.
Will using @RepeatSubmit fail?
When using the @RepeatSubmit annotation to prevent repeated submissions, you may indeed encounter problems that fail in some cases.
Here are some common causes and solutions for failure:
1. Fast continuous clicks on the front end
- Reason: Users quickly click the submit button in a short period of time, resulting in the backend being unable to respond and process in a timely manner.
- Solution: Front-end disable button:
- After the user clicks the Submit button, immediately disable the button to prevent multiple clicks. Front-end display loading animation: Display loading animation to inform the user that the request is being processed.
2. Network delay
- Cause: Network delay may cause the user to think the request failed and thus submit again.
- Solution: Front-end timeout prompt: Set a reasonable request timeout time and prompt the user after the timeout.
- Backend retry mechanism: Implement the retry mechanism on the backend, but be handled with caution to avoid unlimited retry.
3. Session failure
- Cause: If you use a session to store a timestamp, the session may fail due to timeout or server restart.
- Solution: Use distributed caches: Use distributed caches such as Redis to store timestamps to ensure that they work properly in multi-server environments.
4. Concurrent requests
- Cause: In a high concurrency environment, multiple requests may arrive at the same time, resulting in the timestamp check invalidation.
- Solution: Thread-safe: Use thread-safe data structures such as ConcurrentHashMap or AtomicLong to store timestamps.
- Distributed lock: In a distributed environment, distributed locks (such as Redis distributed locks) are used to ensure that the read and write timestamp operations are atomic.
5. Timestamp accuracy issue
- Cause: The accuracy of the timestamp may not be high enough, resulting in multiple requests being considered the same request within a short period of time.
- Solution: Improve timestamp accuracy: Use higher precision timestamps (such as nanoseconds) to reduce conflicts.
6. Code logic error
- Cause: Logical errors in the facet or interceptor may cause the @RepeatSubmit annotation to be invalid.
- Solution: Code Review: Carefully review the code of the section or interceptor to ensure the logic is correct.
- Unit Testing: Write unit tests that cover various boundary situations and make sure the @RepeatSubmit annotation works as expected.
Summarize
The above is personal experience. I hope you can give you a reference and I hope you can support me more.