@Async方法调用JwtTokenUtils抛出No thread-bound request found异常求助
我在使用JwtTokenUtils工具类获取token时发现,普通服务类中调用正常,但在带有@Async注解的服务类方法里调用时,会抛出java.lang.IllegalStateException: No thread-bound request found异常。两个类均通过@Autowired注入JwtTokenUtils,唯一区别就是目标服务方法是否加了@Async注解。
普通服务类在方法启动初期调用工具类即可正常获取结果,而带@Async的服务类是在业务校验后发起API调用时才调用该工具类,期望实现所有场景下都能正常获取token。
抛出异常的代码片段
public String getAuthorizationHeader() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); if(request != null) { return request.getHeader("Authorization"); } else { throw new BadRequestException(ErrorConstants.INVALID_REQUEST); } }
异常详情
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
完整JwtTokenUtils类代码
@Component public class JwtTokenUtils { private final Logger log = LoggerFactory.getLogger(JwtTokenUtils.class); @Value("${security.jwt.token.secret-key:secret-key}") private String secretKey; @PostConstruct protected void init() { secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes()); } public String getToken() { String authToken = getAuthorizationHeader(); if (authToken != null && authToken.startsWith("Bearer ")) { return authToken.substring(7); } else { throw new BadRequestException(ErrorConstants.INVALID_REQUEST, "No/Invalid Authorization Token Provided"); } } public String getAuthorizationHeader() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); if(request != null) { return request.getHeader("Authorization"); } else { throw new BadRequestException(ErrorConstants.INVALID_REQUEST); } } }
原因分析
@Async方法会在独立的线程中执行,而RequestContextHolder默认仅将请求上下文绑定在处理请求的原始线程中,异步线程无法继承该上下文,因此调用currentRequestAttributes()时找不到请求对象,抛出异常。
解决办法
方法1:配置请求上下文继承
在Spring Boot中,自定义异步任务执行器,让异步线程继承原始线程的请求上下文:
@Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); // 核心配置:绑定原始请求上下文到异步线程 executor.setTaskDecorator(runnable -> { RequestAttributes context = RequestContextHolder.currentRequestAttributes(); return () -> { try { RequestContextHolder.setRequestAttributes(context); runnable.run(); } finally { RequestContextHolder.resetRequestAttributes(); } }; }); executor.initialize(); return executor; } }
配置后,异步线程可携带原始请求上下文,调用JwtTokenUtils时能正常获取请求头。
方法2:手动传递token(更推荐)
依赖线程上下文的方式存在隐患(如异步任务执行过长导致上下文被清理),更稳妥的方式是在调用异步方法时直接传递token:
- 修改异步服务方法,接收token参数:
@Async public void asyncBusinessMethod(String token) { // 业务校验后直接使用传入的token发起API调用 String validToken = jwtTokenUtils.validateToken(token); // 后续业务逻辑 }
- 在原始请求线程中获取token并传递:
// 先在主线程获取token String token = jwtTokenUtils.getToken(); // 调用异步方法时传入 asyncService.asyncBusinessMethod(token);
- 为JwtTokenUtils新增token校验方法,统一逻辑:
public String validateToken(String token) { if (token != null && token.startsWith("Bearer ")) { return token.substring(7); } else { throw new BadRequestException(ErrorConstants.INVALID_REQUEST, "No/Invalid Authorization Token Provided"); } }
方法3:配置RequestContextFilter(传统Spring MVC项目)
在web.xml中配置过滤器,允许子线程继承请求上下文:
<filter> <filter-name>requestContextFilter</filter-name> <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> <init-param> <param-name>threadContextInheritable</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>requestContextFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
内容的提问来源于stack exchange,提问作者Shalini Gupta

