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.