Best practice of springboot + aop, no longer afraid to double-click 666

Keywords: Database less Java Google JDK

Talk nineteen to the dozen

During the crazy overtime work less than a month after returning to work, I haven't had much time to write blog recently, which is a little bit slow. Fortunately, in the projects I do, not all are business logic of addition, deletion and modification, or some more interesting and practical technical points, so I sorted them out and shared them with you. Compared with those big guys who are engaged in high-end technology, we are just a yard farmer who is still moving bricks.

Have you ever encountered such a situation: due to the speed of the Internet and other reasons, the response of the web page is very slow. After submitting a form, you find that the service has not responded for a long time, and then you click the submit button crazily (12306 is often so angry). If you have done anti duplicate submission, it's OK, otherwise it's a disaster of what level...

Today, it is mainly to generate a local lock by using the Cache in the user-defined annotation, AOP and Guava package to achieve the anti duplicate submission effect. The overall implementation is relatively simple, not too difficult, and the code is relatively small. Because it is memory based Cache, this implementation method is not suitable for distributed services. It aims to introduce a scheme to prevent repeated submission. If there is anything wrong, you can tear it up gently. After all, they are still in their early 20s and 30s.

What is Guava?

What is a guava bag? A little partner who has done JAVA should know more or less. It is a kind of toolkit developed by google that "dislikes" JAVA's class library and is not easy to use. It has made a good expansion of JDK tools. For example: concurrent, Caches, functional idoms, Strings processing, etc.

In a word, guava bag is very easy to understand. If we can't make wheels, we'll try not to make them, because the wheels we make may not be round. Let's see the implementation.

Code roll up

1. Introduce Guava package dependency

The first step is to introduce Guava's dependency package. There's nothing to say

<dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>21.0</version>
</dependency>
2. Custom annotation

The server-side implementation prevents repeated submission. Generally, it uses the AOP custom annotation method to act on the controller's entry method. Customize a LocalLock annotation for methods that need to prevent duplicate commits.

/**
 * Notes for locks
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LocalLock {

    /**
     * @author fly
     */
    String key() default "";
}

After the annotation is defined, the concrete implementation of AOP interceptor facet needs to be done. In the interceptor() method, Around (surround enhancement) is used, so all annotations with LocalLock will be faceted;

    @Around("execution(public * *(..)) && @annotation(com.chengxy.annotation.LocalLock)")

Since it's a cache, the next attribute must have an expiration time. Set the expiration time of the cache through expireAfterWrite, and the number of caches by maximumSize.

It is similar to the setNX method of Redis to determine whether to commit again by querying whether the key exists in memory.

Here we set the same method, and only one request with the same parameters can be executed in 5 seconds.

How to use this annotation?

@Aspect
@Configuration
public class LockMethodInterceptor {

    private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder()
            // Maximum cache 100
            .maximumSize(1000)
            // Expire 5 seconds after setting write cache
            .expireAfterWrite(5, TimeUnit.SECONDS)
            .build();

    @Around("execution(public * *(..)) && @annotation(com.chengxy.annotation.LocalLock)")
    public Object interceptor(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        LocalLock localLock = method.getAnnotation(LocalLock.class);
        String key = getKey(localLock.key(), pjp.getArgs());
        if (!StringUtils.isEmpty(key)) {
            if (CACHES.getIfPresent(key) != null) {
                throw new RuntimeException("Do not repeat request");
            }
            // If it is the first request, the current key object will be pressed into the cache
            CACHES.put(key, key);
        }
        try {
            return pjp.proceed();
        } catch (Throwable throwable) {
            throw new RuntimeException("Server exception");
        } finally {
            // TODO to demonstrate the effect, we will not call cache.invalidate (key); the code
        }
    }

    /**
     * key If you want to be flexible, you can write it as an interface and implementation class (TODO will explain later)
     *
     * @param keyExpress Expression
     * @param args       parameter
     * @return Generated key
     */
    private String getKey(String keyExpress, Object[] args) {
        for (int i = 0; i < args.length; i++) {
            keyExpress = keyExpress.replace("arg[" + i + "]", args[i].toString());
        }
        return keyExpress;
    }
}
3. Implementation of control layer

We add the annotation to the control layer method. key = "city:arg[0] key is defined by itself. arg[0] is the first parameter instead of the matching rule. Then the city:token can't be submitted repeatedly for a certain period of time

@RestController
@RequestMapping("/city")
public class BookController {

    @LocalLock(key = "city:arg[0]")
    @GetMapping
    public String query(@RequestParam String token) {
        return "ok- " + token;
    }
}
4, test

Next, let's test the expected results: only the first submission will return normally within 5 seconds, and the rest will display "do not submit repeatedly". See if the execution results are the same as we expected. Here use postman test.

First request for normal response:

The second time after the request, the result "repeated submission" is returned. Obviously, we have achieved success

Little welfare:

Get some geek courses, Shh ~, free for the kids. Official number [programmer's internal affairs] reply to [geek] to collect it by yourself

Posted by betportal on Sun, 12 Apr 2020 04:25:05 -0700