Spring — проблемы с асинхронным контекстом

Spring

Асинхронность связана с проблемой переключения контекста. Например, объект запроса Spring фактически существует в объекте контекста, и мы можем получить его, при условии, что вся ваша логика выполнения является потоком.ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();Именно для того, чтобы получить контекст, он бы перехватил его в моей логике и ввел.

Но иногда задействованы асинхронные контексты.

@Slf4j
@Service
public class UserService {

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

        if (requestAttributes == null) {
            log.info("当前线程为 {} 位置为{} require对象为空.", Thread.currentThread().getName(),  "service");
        } else {
            HttpServletRequest request = requestAttributes.getRequest();
            log.info("当前线程为 {},请求方法为 {},请求路径为:{} 位置为:{}", Thread.currentThread().getName(), request.getMethod(), request.getRequestURL(), "service");
        }
    }
}

Включить асинхронность, основная программа.

@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("当前线程为 {} 请求方法为 {} 请求路径为{} 位置为{}", Thread.currentThread().getName(), request.getMethod(), request.getRequestURL(),"controller");
        return id;
    }
}

мы нашли журналы

2020-03-30 17:09:14.475  INFO 16720 --- [nio-8080-exec-1] c.e.springasync.SpringAsyncApplication   : 当前线程为 http-nio-8080-exec-1 请求方法为 GET 请求路径为http://localhost:8080/find/111 位置为controller
2020-03-30 17:09:14.480  INFO 16720 --- [cTaskExecutor-1] c.e.springasync.service.UserService      : 当前线程为 SimpleAsyncTaskExecutor-1 位置为service require对象为空.

Таким образом, в настоящее время контекст не существует, поэтому нам нужно настроить его вручную, Spring предоставляет хороший рабочий объект.

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

    @Bean(name = ASYNC_EXECUTOR_NAME)
    public Executor executor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix(ASYNC_EXECUTOR_NAME);
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(5);
        executor.setQueueCapacity(-1);
        // 这里在每次异步调用的时候, 会包装一下.
        executor.setTaskDecorator(runnable -> {
            // 这个时候还是同步状态
            RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
            // 返回的这个 runnable对象 才是去调用线程池.
            return () -> {
                try {
                    // 我们set 进去 ,其实是一个ThreadLocal维护的.
                    RequestContextHolder.setRequestAttributes(requestAttributes);
                    runnable.run();
                } finally {
                    // 最后记得释放内存
                    RequestContextHolder.resetRequestAttributes();
                }
            };
        });
        return executor;
    }
}

Итак, в это время

2020-03-30 17:10:48.377  INFO 18060 --- [nio-8080-exec-2] c.e.springasync.SpringAsyncApplication   : 当前线程为 http-nio-8080-exec-2 请求方法为 GET 请求路径为http://localhost:8080/find/111 位置为controller
2020-03-30 17:10:48.382  INFO 18060 --- [ asyncExecutor1] c.e.springasync.service.UserService      : 当前线程为 asyncExecutor1,请求方法为 GET,请求路径为:http://localhost:8080/find/111 位置为:service

Это решение.