java - 在 Springboot 应用程序中使用带有 Rest 模板调用的 @Async 注释的方法
问题描述
@Async 和 Rest Template 调用有问题;这是我的主应用程序类,带有任务执行器 Bean 和 EnableAsync Annotation
@SpringBootApplication
@ComponentScan({"org.***"})
@EnableAspectJAutoProxy
@EnableAsync
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})
@EnableFeignClients(basePackages = {"org.service.feign"})
public class MainApplication extends SpringBootServletInitializer {
/**
* <p>main.</p>
*
* @param args an array of {@link java.lang.String} objects.
*/
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
@Bean(name = "threadPoolTaskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix(“CustomLookup-");
executor.initialize();
return executor;
}
/**
* Configure.
*
* @param application the application
* @return the spring application builder
*/
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MainApplication.class);
}
/**
* <p>requestContextListener.</p>
*
* @return a {@link org.springframework.web.context.request.RequestContextListener} object.
*/
@Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
}
这是我在 Rest Controller 中调用的带有异步注释的测试服务:
@Service
public class TestService {
@Async("threadPoolTaskExecutor")
public ResponseEntity<Object> test()
throws Exception{
PagedRequest<SearchRequest> request = new PagedRequest<SearchRequest>();
SearchRequest filters = new Request();
filters.setCod(“abcdeg");
request.setFilters(filters);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(new RestInterceptor())); // here I set a custom headers
final HttpHeaders theJsonHeader = new HttpHeaders();
theJsonHeader.setContentType(MediaType.APPLICATION_JSON);
final MultiValueMap<String, Object> theMultipartRequest = new LinkedMultiValueMap<>();
ObjectMapper objectMapper = new ObjectMapper();
String someJsonString = objectMapper.writeValueAsString(request);
theMultipartRequest.add("request", new HttpEntity<>(someJsonString, theJsonHeader));
ResponseEntity<Object> response = null;
final HttpEntity<PagedRequest<SearchRequest>> theHttpEntity = new HttpEntity<>(request, theJsonHeader);
String path = “http://...”; //url removed for privacy
response = restTemplate.postForEntity(path, theHttpEntity, Object.class);
return response;
}
}
此服务在其余模板上返回一个 nullPointer;这是堆栈跟踪
java.lang.NullPointerException: null
at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:61) ~[spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:773) ~[spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:743) ~[spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:677) ~[spring-web-5.2.3.RELEASE.jar:5.2.3.RELEASE]
at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:452) ~[spring-web-5.2.3.RELEASE.jar:5.2.3.REL
这是我在 RestTemplate 中添加的 RestIntercept 的代码
public class RestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (requestAttributes == null)
return null;
HttpServletRequest req = requestAttributes.getRequest();
if (req == null || req.getHeader(PrevConstants.USER_KEY)==null || req.getHeader(PrevConstants.JWT_HEADER_NAME)==null)
return null;
String userKey = req.getHeader(PrevConstants.USER_KEY);
String jwt = req.getHeader(PrevConstants.JWT_HEADER_NAME);
if (jwt == null) {
jwt = "custom jwt";
}
else if ( !jwt.startsWith("Bearer")) { jwt = "Bearer " + jwt; }
request.getHeaders().set(PrevConstants.USER_KEY, userKey);
request.getHeaders().set(PrevConstants.JWT_HEADER_NAME, jwt);
ClientHttpResponse response = execution.execute(request, body);
return response;
}
}
但是如果我删除,@EnableAsync 和 @Async 简单的 Rest 模板可以完美地工作。
当我通过 HttpServlet 请求时,Eclipse 调试显示:
解决方案
RequestContextHolder
保存当前线程正在处理的请求的上下文。当您使用@Async
拦截器时,将在与处理请求的线程不同的线程上调用。结果,RequestContextHolder.getRequestAttributes()
返回null
,然后您的拦截器返回null
响应。为了遵守它的合同,ClientHttpRequestInterceptor#intercept
它必须返回一个非空值,所以这个null
响应会导致失败。
如果要使用@Async
,则必须RequestAttributes
在 REST 控制器中检索 ,然后将它们TestService
作为参数传递给test
方法。然后,您可以RestInterceptor
使用属性创建您的,而不是使用它RequestContextHolder
来访问它们:
@Async("threadPoolTaskExecutor")
public ResponseEntity<Object> test(RequestAttributes requestAttributes) throws Exception {
// …
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(new RestInterceptor(requestAttributes)));
// …
}
推荐阅读
- javascript - HTML 音频 - 提供的音量 (-0.1) 超出范围 - 数学最大值不起作用
- python - 如何在不使用 tf 记录的情况下训练 tf 对象检测 api 模型
- node.js - 如果有未提交的 git 更改,NPM 脚本应该会失败
- javascript - 在 JavaScript 中创建 N 维点网格
- node.js - NodeJS 客户端使用服务器流回响应
- javascript - Nuxt,过渡不适用于子路由(使用 NuxtChild 显示)
- forms - 谷歌脚本TextItem标题中的文本样式
- reactjs - MouseEnter/MouseOver 不适用于 react-testing-library。如何使用 react-testing-library 测试悬停事件
- xcode - XCODE:架构 x86_64 的未定义符号:
- android - 如何在firebase实时数据库中动态添加新节点