SpringBoot异步任务获取HttpServletRequest

这篇具有很好参考价值的文章主要介绍了SpringBoot异步任务获取HttpServletRequest。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

在使用框架日常开发中需要在controller中进行一些异步操作减少请求时间,但是发现在使用@Anysc注解后会出现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配置

<!-- 阿里线程共享 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
            <version>2.11.0</version>
        </dependency>

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

public class CommonUtil {
    public static TransmittableThreadLocal<HttpServletRequest> requestTransmittableThreadLocal = new TransmittableThreadLocal<HttpServletRequest>();

    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<String,String> headMap;
    private final HashMap<String,String> requestParamMap;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = CommonUtil.getBodyString(request).getBytes(Charset.forName("UTF-8"));

        headMap = new HashMap();
        Enumeration<String> headNameList = request.getHeaderNames();
        while (headNameList.hasMoreElements()){
            String key = headNameList.nextElement();
            headMap.put(key.toLowerCase(),request.getHeader(key));
        }

        requestParamMap = new HashMap<>();
        Enumeration<String> 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);
        executor.setQueueCapacity(200);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("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注解指定进行共享拦截的任务执行器即可文章来源地址https://www.toymoban.com/news/detail-834156.html

@PostMapping("/testAsync")
    @ResponseBody
    public Object testAsync(@RequestBody Map<String, Object> 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");
    }
}

到了这里,关于SpringBoot异步任务获取HttpServletRequest的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • springboot 与异步任务,定时任务,邮件任务

    在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务,其实,在Spring 3.x之后,就已经内置了@Async来完美解决这个问题。 SpringBoot 实现比较简单 主启

    2024年02月10日
    浏览(32)
  • SpringBoot整合、SpringBoot与异步任务

    java 的代码是同步顺序执行,当我们需要执行异步操作时我们通常会去创建一个新线程去执行。比如new Thread()。start(),或者使用线程池线程池 new ThreadPoolExecutor().execute 。 在 Springboot 中对其进行了简化处理,使用@EnableAsync + @Async 可以快速开启一个异步线程执行任务 启动类上使

    2024年02月13日
    浏览(29)
  • SpringBoot 实现异步任务

    在多数的Java项目中,在很多的场景都是用同步的方式去实现模块间的相互调用,在模块调用间可能会造成一些延迟,本篇文章将使用SpringBoot 去实现异步之间的调用, 提高系统的并发性能、用户体验。 简单直观:同步任务的执行是顺序的,代码执行的流程清晰明了,易于理

    2024年02月10日
    浏览(28)
  • SpringBoot(十三)异步任务

    目录 异步任务 1.1 什么叫异步 1、Java线程处理 2、SpringBoot异步任务 2.1 使用注解@EnableAsync开启异步任务支持 2.2、使用@Async注解标记要进行异步执行的方法 2.3、controller测试 3、异步任务相关限制 4、自定义 Executor(自定义线程池) 4.1、应用层级: 4.2、方法层级: 有时候,前端

    2024年02月04日
    浏览(27)
  • SpringBoot 异步、邮件任务

    创建一个Hello项目 创建一个类AsyncService 异步处理还是非常常用的,比如我们在网站上发送邮件,后台会去发送邮件,此时前台会造成响应不动,直到邮件发送完毕,响应才会成功,所以我们一般会采用多线程的方式去处理这些任务。 编写方法,假装正在处理数据,使用线程

    2024年02月13日
    浏览(37)
  • SpringBoot(19)异步任务

    有时候,前端可能提交了一个耗时任务,如果后端接收到请求后,直接执行该耗时任务,那么前端需要等待很久一段时间才能接受到响应。如果该耗时任务是通过浏览器直接进行请求,那么浏览器页面会一直处于转圈等待状态。 事实上,当后端要处理一个耗时任务时,通常都

    2024年02月16日
    浏览(27)
  • python异步切片下载文件(内置redis获取任务 mongo更新任务状态等)

    异步切片下载二进制文件并上传桶删除本地文件 部分代码来源于y小白的笔记

    2024年01月19日
    浏览(89)
  • 使用多线程执行任务,并获取返回结果,附异步实现

    这里创建了一个包含三个线程的固定线程池 线程池的介绍 根据主机情况实现自定义线程池: 也可以通过继承 ThreadPoolExecutor 类来实现一个自定义线程池工具类。ThreadPoolExecutor 是 Java 标准库中提供的一个线程池实现,通过继承它,我们可以实现自定义的线程池。 下面是一个继

    2024年02月16日
    浏览(28)
  • SpringBoot原理分析 | 任务:异步、邮件、定时

    💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! 异步任务 Java异步指的是在程序执行过程中,某些任务可以在后台进行,而不会阻塞程序的执行。通常情况下,Java异步使用线程池来实现,将任务放入线程池中,等待线程池中的线程执行这些任务。Java异步可以提高程

    2024年02月16日
    浏览(34)
  • SpringBoot异步任务及并行事务实现

            上一篇介绍了原生Java如何实现串行/并行任务,主要使用了线程池 + Future + CountDownLatch,让主线程等待子线程返回后再向下进行。而在SpringBoot中,利用@Async和AOP对异步任务提供了更加便捷的支持,下面就针对SpringBoot使用异步任务需要注意的细节做一些分析。      

    2024年02月02日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包