Asynchrony involves a context-switching problem, like Spring’s Request object actually exists in a context object that we can fetch, provided that all of your execution logic is a thread. Like ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder. GetRequestAttributes (); Is to get the context. He will intercept in our logic and inject.

But asynchronous contexts are sometimes involved.

@Slf4j
@Service
public class UserService {

    @Async
    public void find(a) {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        if (requestAttributes == null) {
            log.info("Current thread is {} position is {} require object is empty.", Thread.currentThread().getName(),  "service");
        } else {
            HttpServletRequest request = requestAttributes.getRequest();
            log.info("Current thread is {}, request method is {}, request path is :{} location is :{}", Thread.currentThread().getName(), request.getMethod(), request.getRequestURL(), "service"); }}}Copy the code

Open asynchrony, main program.

@Slf4j
@EnableAsync
@RestController
@SpringBootApplication
public class SpringAsyncApplication {
    private final UserService service;

    @Autowired
    public SpringAsyncApplication(UserService service) {
        this.service = service;
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringAsyncApplication.class, args);
    }

    @GetMapping("/find/{id}")
    public String echo(@PathVariable(value = "id") String id) {
        service.find();
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        log.info("Current thread is {} request method is {} request path is {} position is {}", Thread.currentThread().getName(), request.getMethod(), request.getRequestURL(),"controller");
        returnid; }}Copy the code

We found that the logs

2020-03-30 17:09:14.475  INFO 16720 --- [nio-8080-exec-1] C.E.S pringasync. SpringAsyncApplication: - nio - the current thread for HTTP8080-exec-1The request method is GET and the request path is HTTP:/ / localhost: 8080 / find / 111 position for the controller
2020-03-30 17:09:14.480  INFO 16720 --- [cTaskExecutor-1] C.E.S pringasync. Service. UserService: the current thread for SimpleAsyncTaskExecutor -1The position is Service Require object is empty.Copy the code

So the context doesn’t exist at this point. So we need to configure it manually. Spring provides a good way to manipulate objects.

@Configuration
public class Config {
    private static final String ASYNC_EXECUTOR_NAME = "asyncExecutor";

    @Bean(name = ASYNC_EXECUTOR_NAME)
    public Executor executor(a) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix(ASYNC_EXECUTOR_NAME);
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(-1);
        // This is wrapped each time an asynchronous call is made.
        executor.setTaskDecorator(runnable -> {
            // It is synchronized
            RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
            // Return the runnable object to call the thread pool.
            return() - > {try {
                    // we set it to a ThreadLocal.
                    RequestContextHolder.setRequestAttributes(requestAttributes);
                    runnable.run();
                } finally {
                    // Finally remember to free memoryRequestContextHolder.resetRequestAttributes(); }}; });returnexecutor; }}Copy the code

So at this time

2020-03-30 17:10:48.377  INFO 18060 --- [nio-8080-exec-2] C.E.S pringasync. SpringAsyncApplication: - nio - the current thread for HTTP8080-exec-2The request method is GET and the request path is HTTP:/ / localhost: 8080 / find / 111 position for the controller
2020-03-30 17:10:48.382  INFO 18060- [asyncExecutor1] C.E.S pringasync. Service. UserService: the current thread for asyncExecutor1, method to GET the request, the request path is: HTTP:/ / localhost: 8080 / find / 111 position is: the service
Copy the code

This is a solution.