解决Springboot中@Async注解获取不到上下文信息问题
作者:飞翔的佩奇
问题描述
springboot项目中,需要使用到异步调用某个方法,此时 第一个想到的就是 @Async 注解,但是 发现 方法执行报错了,具体报错如下:
java.lang.NullPointerException
at com.ruoyi.common.utils.ServletUtils.getRequest(ServletUtils.java:56)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:782)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:717)
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:443)
at com.ruoyi.web.ecs.service.impl.EcsCollectOperateServiceImpl.collect(EcsCollectOperateServiceImpl.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:90)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
at java.lang.reflect.Method.invoke(Method.java:508)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
at java.util.concurrent.FutureTask.run(FutureTask.java:277)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1160)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.lang.Thread.run(Thread.java:825)
上面日志有点多,其实核心就是这一部分日志:
java.lang.NullPointerException
at com.ruoyi.common.utils.ServletUtils.getRequest(ServletUtils.java:56)
这块逻辑就是,使用spring底层提供的获取上下文信息的方法。
所以说明 获取不到上下文信息,结果导致报错
解决办法
- 对自定义的配置类 进行改造,原来的配置类是这样的:
import java.lang.reflect.Method; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; @Configuration @EnableAsync public class AsyncConfiguration { private static final Logger logger = LoggerFactory.getLogger(AsyncConfiguration.class); @Bean(name = "taskExecutor") public Executor getAsyncExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); // 核心线程数:线程池创建时候初始化的线程数 taskExecutor.setCorePoolSize(10); // 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程 taskExecutor.setMaxPoolSize(20); // 缓冲队列:用来缓冲执行任务的队列 taskExecutor.setQueueCapacity(50); // 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁 taskExecutor.setKeepAliveSeconds(60); // 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池 taskExecutor.setThreadNamePrefix("HiTask-"); // 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程) //taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); taskExecutor.initialize(); return taskExecutor; } class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { @Override public void handleUncaughtException(Throwable throwable, Method method, Object... objects) { logger.error("MethodName={},Throwable={}",method.getName(),throwable.toString()); } } }
- 改造之后:
import java.lang.reflect.Method; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; @Configuration @EnableAsync public class AsyncConfiguration { private static final Logger logger = LoggerFactory.getLogger(AsyncConfiguration.class); @Bean(name = "taskExecutor") public Executor getAsyncExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); // 核心线程数:线程池创建时候初始化的线程数 taskExecutor.setCorePoolSize(10); // 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程 taskExecutor.setMaxPoolSize(20); // 缓冲队列:用来缓冲执行任务的队列 taskExecutor.setQueueCapacity(50); // 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁 taskExecutor.setKeepAliveSeconds(60); // 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池 taskExecutor.setThreadNamePrefix("HiTask-"); // 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程) //taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); taskExecutor.initialize(); //解决使用@Async注解,获取不到上下文信息的问题 taskExecutor.setTaskDecorator(runnable -> { RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); return ()->{ try { // 我们set 进去 ,其实是一个ThreadLocal维护的. RequestContextHolder.setRequestAttributes(requestAttributes); runnable.run(); } finally { // 最后记得释放内存 RequestContextHolder.resetRequestAttributes(); } }; }); return taskExecutor; } class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler { @Override public void handleUncaughtException(Throwable throwable, Method method, Object... objects) { logger.error("MethodName={},Throwable={}",method.getName(),throwable.toString()); } } }
总结
解决使用@Async注解,获取不到上下文信息的问题,只需要增加这一段代码即可
taskExecutor.setTaskDecorator(runnable -> { RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); return ()->{ try { // 我们set 进去 ,其实是一个ThreadLocal维护的. RequestContextHolder.setRequestAttributes(requestAttributes); runnable.run(); } finally { // 最后记得释放内存 RequestContextHolder.resetRequestAttributes(); } }; });
额外补充一点
如果使用 @Async注解,发现没有生效,那有可能 你没有加 @EnableAsync 注解。
@EnableAsync注解表示 开启异步任务,可以写在springboot的启动类上,也可以写在 配置类上
到此这篇关于解决Springboot中@Async注解获取不到上下文信息问题的文章就介绍到这了,更多相关Springboot @Async获取上下文内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!