SpringBoot详细讲解异步任务如何获取HttpServletRequest

网友投稿 1155 2022-08-02


目录原因分析解决方案前置条件pom配置requrest共享自定义request过滤器自定义任务执行器调用示例

原因分析

@Anysc注解会开启一个新的线程,主线程的Request和子线程是不共享的,所以获取为null在使用springboot的自定带的线程共享后,代码如下,Request不为null,但是偶发的其中body/head/urlparam内容出现获取不到的情况,是因为异步任务在未执行完毕的情况下,主线程已经返回,拷贝共享的Request对象数据被清空

ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

//设置子线程共享

RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);

HttpServletRequest request = servletRequestAttributes.getRequest();

解决方案

前置条件

启动类添加@EnableAsync注解标记@Async的异步方法不能和调用者在同一个class中

pom配置

com.alibaba

transmittable-thread-local

2.11.0

requrest共享

通过TransmittableThreadLocal对象进行线程对象共享

public class CommonUtil {

public static TransmittableThreadLocal requestTransmittableThreadLocal = new TransmittableThreadLocal();

public static void shareRequest(HttpServletRequest request){

requestTransmittableThreadLocal.set(request);

}

public static HttpServletRequest getRequest(){

HttpServletRequest request = requestTransmittableThreadLocal.get();

if(request!=null){

return requestTransmittableThreadLocal.get();

}else{

ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

if(requestAttributes!=null){

return requestAttributes.getRequest();

}else{

return null;

}

}

}

public static void remove(){

requestTransmittableThreadLocal.remove();

}

}

注:系统中所有Request获取需要统一从CommonUtil指定来源,例如token鉴权等

自定义request过滤器

通过自定义过滤器对Request的内容进行备份保存,主线程结束时Request清除结束不会影响到子线程的相应参数的获取,也适用于增加拦截器/过滤器后body参数无法重复获取的问题。需要注意的是对header参数处理时key要忽略大小写

public class HttpServletRequestReplacedFilter implements Filter, Ordered {

@Override

public void destroy() {

}

@Override

public void doFilter(ServletRequest request, ServletResponse response,

FilterChain chain) throws IOException, ServletException {

ServletRequest requestWrapper = null;

if (request instanceof HttpServletRequest) {

requestWrapper = new RequestWrapper((HttpServletRequest) request);

}

//获取请求中的流如何,将取出来的字符串,再次转换成流,然后把它放入到新request对象中。

// 在chain.doFiler方法中传递新的request对象

if (requestWrapper == null) {

chain.doFilter(request, response);

} else {

chain.doFilter(requestWrapper, response);

}

}

@Override

public void init(FilterConfig arg0) throws ServletException {

}

@Override

public int getOrder() {

return 10;

}

}

public class RequestWrapper extends HttpServletRequestWrapper{

private final byte[] body;

private final HashMap headMap;

private final HashMap requestParamMap;

public RequestWrapper(HttpServletRequest request) throws IOException {

super(request);

body = CommonUtil.getBodyString(request).getBytes(Charset.forName("UTF-8"));

headMap = new HashMap();

Enumeration headNameList = request.getHeaderNames();

while (headNameList.hasMoreElements()){

String key = headNameList.nextElement();

headMap.put(key.toLowerCase(),request.getHeader(key));

}

requestParamMap = new HashMap<>();

Enumeration parameterNameList = request.getParameterNames();

while (parameterNameList.hasMoreElements()){

String key = parameterNameList.nextElement();

requestParamMap.put(key,request.getParameter(key));

}

}

@Override

public BufferedReader getReader() throws IOException {

return new BufferedReader(new InputStreamReader(getInputStream()));

}

@Override

public ServletInputStream getInputStream() throws IOException {

final ByteArrayInputStream bais = new ByteArrayInputStream(body);

return new ServletInputStream() {

@Override

public int read() throws IOException {

return bais.read();

}

@Override

public boolean isFinished() {

return false;

}

@Override

public boolean isReady() {

return false;

}

@Override

public void setReadListener(ReadListener readListener) {

}

};

}

@Override

public String getHeader(String name) {

return headMap.get(name.toLowerCase());

}

@Override

public String getParameter(String name) {

return requestParamMap.get(name);

}

}

自定义任务执行器

用于拦截异步任务执行,在任务执前统一进行Request共享操作,且可以定义多个,不影响原有的异步任务代码

public class CustomTaskDecorator implements TaskDecorator {

@Override

public Runnable decorate(Runnable runnable) {

ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

HttpServletRequest request = requestAttributes.getRequest();

System.out.println("异步任务共享request");

return () -> {

try {

CommonUtil.shareRequest(request);

runnable.run();

} finally {

CommonUtil.remove();

}

};

}

}

@Configuration

public class TaskExecutorConfig {

@Bean()

public Executor taskExecutor() {

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

executor.setCorePoolSize(10);

executor.setMaxPoolSize(20);

http:// executor.setQueueCapacity(200);

executor.setKeepAliveSeconds(60);

executor.setThreadNamePhttp://refix("taskExecutor-");

executor.setAwaitTerminationSeconds(60);

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

executor.initialize();

return executor;

}

@Bean("shareTaskExecutor")

public Executor hpTaskExecutor() {

ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

executor.setCorePoolSize(10);

executor.setMaxPoolSize(20);

executor.setQueueCapacity(200);

executor.setKeepAliveSeconds(60);

executor.setThreadNamePrefix("shareTaskExecutor-");

executor.setWaitForTasksToCompleteOnShutdown(true);

executor.setAwaitTerminationSeconds(60);

// 增加 TaskDecorator 属性的配置

executor.setTaskDecorator(new CustomTaskDecorator());

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

executor.initialize();

return executor;

}

}

调用示例

给@Anysc注解指定进行共享拦截的任务执行器即可

@PostMapping("/testAsync")

@ResponseBody

public Object testAsync(@RequestBody Map params) throws Exception{

Result result = Result.okResult();

asyncUtil.executeAsync();

return result;

}

@Component

public class AsyncUtil {

@Async("shareTaskExecutor")

public void executeAsync () throws InterruptedException {

System.out.println("开始执行executeAsync");

Thread.sleep(3000);

System.out.println("结束执行executeAsync");

}

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringBoot详细讲解通过自定义classloader加密保护class文件
下一篇:Java中Excel高效解析工具EasyExcel的实践(java操作Excel)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~