解决Springboot中@Async注解获取不到上下文信息问题

 更新时间:2024年01月15日 11:49:46   作者:飞翔的佩奇  
实际开发中我们经常需要通过spring上下文获取一些配置信息,本文主要介绍了解决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获取上下文内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot3 使用 jasypt 加密配置文件的使用步骤

    springboot3 使用 jasypt 加密配置文件的使用步骤

    在SpringBoot项目中,使用Jasypt加密配置文件可以有效保护敏感信息,首先,需添加Jasypt依赖并配置加密密码,可在application.properties或通过启动参数、环境变量设置,本文介绍了Jasypt的配置步骤及使用方法,帮助开发者保护应用配置信息
    2024-11-11
  • 如何使用Gradle实现类似Maven的profiles功能

    如何使用Gradle实现类似Maven的profiles功能

    这篇文章主要介绍了如何使用Gradle实现类似Maven的profiles功能,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-06-06
  • Java Shutdown Hook场景使用及源码分析

    Java Shutdown Hook场景使用及源码分析

    shutdown hook 就是一个简单的已初始化但是未启动的线程,本文详细的介绍了Java Shutdown Hook场景使用及源码分析,感兴趣的朋友可以参考一下
    2021-06-06
  • 解决feign接口返回泛型设置属性为null的问题

    解决feign接口返回泛型设置属性为null的问题

    这篇文章主要介绍了解决feign接口返回泛型设置属性为null的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Spring应用中使用acutator/refresh刷新属性不生效的问题分析及解决

    Spring应用中使用acutator/refresh刷新属性不生效的问题分析及解决

    在Spring应用收到/actuator/refresh的POST请求后,标注了@RefreshScope以及@ConfiguratioinProperties的bean会被Spring容器重新加载,但是,在实际应用中,并没有按照预期被Spring容器加载,本文将讨论导致这种未按预期刷新的一种原因,感兴趣的朋友可以参考下
    2024-01-01
  • mybatis plus in方法使用说明

    mybatis plus in方法使用说明

    这篇文章主要介绍了mybatis plus in方法使用说明,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-11-11
  • JAVA语法糖原理你知道吗

    JAVA语法糖原理你知道吗

    语法糖(Syntactic sugar),也叫做糖衣语法,是英国科学家发明的一个术语,通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会.这篇文章主要介绍了Java 中的语法糖知识,需要的朋友可以参考下
    2021-09-09
  • springboot项目实现断点续传功能

    springboot项目实现断点续传功能

    这篇文章主要介绍了springboot项目实现断点续传,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • jdk8的datetime时间函数使用示例

    jdk8的datetime时间函数使用示例

    这篇文章主要介绍了jdk8的datetime时间函数使用示例,需要的朋友可以参考下
    2014-03-03
  • java easyUI实现自定义网格视图实例代码

    java easyUI实现自定义网格视图实例代码

    这篇文章主要给大家介绍了关于java easyUI实现自定义网格视图的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-10-10

最新评论