SpringBoot asynchronous context transfer between threads

  • 2021-12-12 08:51:23
  • OfStack

Directory asynchronous thread transfer context requirements implementation enable asynchronous function configuration asynchronous configuration task decorator enable multithread safety context can not share problems between threads problem solution principle results

Transfer context between asynchronous threads

Demand

In SpringBoot projects, @ Async is often used to open a child thread to complete asynchronous operations. User information in the main thread needs to be passed to the child threads

Realization

Enable asynchronous functionality

Add @ EnableAsync annotation to the startup class


@EnableAsync
@SpringBootApplication
public class Application {}

Configure asynchronous

Create a new configuration class, implement AsyncConfigurer interface, and override getAsyncExecutor method


@Configuration
public class AsyncConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setThreadNamePrefix("async-pool-");
        //  This 1 Step is key, asynchronous Task Decorator 
        executor.setTaskDecorator(new MyContextDecorator());
        executor.initialize();
        return executor;
    }
}

Configure the task decorator

Create a new asynchronous task decorator, implement decorate interface, and rewrite decorate method


public class MyContextDecorator implements TaskDecorator {
    @Override
    @Nonnull
    public Runnable decorate(@Nonnull Runnable runnable) {
  //  Get the request information in the main thread (our user information is also placed in it) 
       RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return () -> {
            try {
               //  Set the request information of the main thread to the child thread 
               RequestContextHolder.setRequestAttributes(attributes);
              //  Execute child threads, which 1 Don't forget the steps 
                runnable.run();
            } finally {
             //  When the thread ends, empty the information, otherwise it may cause memory leakage 
                RequestContextHolder.resetRequestAttributes();
            }
        };
    }

Added: RequestContextHolder is internally based on ThreadLocal, so when using set get, it is bound to the current thread. Of course, the user's user information is not necessarily placed in RequestContextHolder, and readers can expand it by themselves.

At this point, the Request information in the parent thread can be obtained normally through the child thread opened by @ Async.

Enabling multithread safety context cannot share between threads

Problem

Project multi-thread add data, mybatisplus metadata filling function, fill the creator, the data is from spring security SecurityContextHolder. getContext. getAuthentication, synchronous operation, can be normal access, and asynchronous execution of time-space pointer exception.

Solutions

Configuring Security Context Global Policy SecurityContextHolder. setStrategyName (SecurityContextHolder.MODE_INHERITABLETHREADLOCAL)

Principle

The Spring Security security context default policy is MODE_THREADLOCAL, and the ThreadLocal mechanism holds the security context for each consumer.

This means that, as long as logical execution against a consumer takes place in the same thread, each method can obtain its security context through the SecurityContextHolder tool, even if it does not pass its security context as a parameter between the methods.

This way of using ThreadLocal is safe as long as you pay attention to clearing the security context in ThreadLocal after processing the current user's request.

MODE_GLOBAL All threads in JVM use the same security context MODE_INHERITABLETHREADLOCAL Some applications will have their own threads created, and it is hoped that these new threads will also use the creator's security context. This effect can be achieved by configuring SecurityContextHolder as an MODE_INHERITABLETHREADLOCAL policy.

Results

In the configuration file, add:


@PostConstruct
public void init(){
    SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
 

@ PostConstruct annotation Many people think it is provided by Spring. It's actually Java's own annotation.

Explanation of this annotation in Java: @ PostConstruct This annotation is used to decorate a non-static void () method. The @ PostConstruct modified method runs when the server loads Servlet and is executed only once by the server. PostConstruct is executed after the constructor and before the init () method.

Normally we will use the @ PostConstruct annotation in the Spring framework. The annotation's method is executed in the whole Bean initialization sequence:

Constructor (construction method)- > @ Autowired (dependency injection)- > @ PostConstruct (annotated method)


Related articles: