当前位置:   article > 正文

Spring Boot 异步线程静态获取request对象为空 RequestContextHolder 为空 Java 异步线程获取request为空_requestcontextholder.setrequestattributes(requestc

requestcontextholder.setrequestattributes(requestcontextholder.getrequestatt

Spring Boot 异步线程静态获取request对象为空 RequestContextHolder 为空 Java 异步线程获取request为空

一、问题描述

        在Spring Boot的web项目中,采用静态获取request对象时,发现无法获取到request对象,而获取的 RequestContextHolder 对象为空,抛出 NPE 异常 ...

  1. public static HttpServletRequest getRequest() {
  2. ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
  3. HttpServletRequest request = servletRequestAttributes.getRequest();
  4. return request ;
  5. }

        经过排查代码,发现是在异步线程中,静态获取request对象,导致获取不到,从而抛出NPE异常...

二、模拟实现

        1、演示:异步线程中无法获取到request对象,抛出NPE异常

  1. @RequestMapping("/req")
  2. public String req(){
  3. ExecutorService executor = Executors.newFixedThreadPool(2);
  4. executor.submit(()->{
  5. log.info(Thread.currentThread().getName() + " start ===>");
  6. String token = null;
  7. try {
  8. Thread.sleep(1000);
  9. token = RequestUtil.getRequest().getHeader("token");
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }catch (Exception e){
  13. /**
  14. * 注意: 需要增加 catch Exception 异常 ;
  15. * 否则: RequestUtil.getRequest() 的 NPE 异常无法抛出!
  16. */
  17. e.printStackTrace();
  18. }
  19. log.info(Thread.currentThread().getName() + " end token ===>{}", token);
  20. });
  21. return "ok";
  22. }

        2、输出结果如下:

  1. INFO] com.runcode.springboottourist.RequController:40 : pool-8-thread-1 start ===>
  2. java.lang.NullPointerException
  3. at com.runcode.springboottourist.util.RequestUtil.getRequest(RequestUtil.java:29)
  4. at com.runcode.springboottourist.RequController.lambda$req$0(RequController.java:44)
  5. at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
  6. at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  7. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
  8. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
  9. at java.lang.Thread.run(Thread.java:745)
  10. [INFO] com.runcode.springboottourist.RequController:54 : pool-8-thread-1 end token ===>null

三、解决

        1、只需要设置 request 对象可以在子线程中共享即可,在 主线程代码部分设置即可。

RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(),true);

        2、完整代码参考如下:

  1. @RequestMapping("/req/fix")
  2. public String reqFix(){
  3. // 设置request 对象在,子线程(异步线程)中可以共享
  4. RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(),true);
  5. HttpServletRequest request = RequestUtil.getRequest();
  6. ExecutorService executor = Executors.newFixedThreadPool(2);
  7. executor.submit(()->{
  8. log.info(Thread.currentThread().getName() + " start ===>");
  9. String token = null;
  10. try {
  11. Thread.sleep(1000);
  12. token = request.getHeader("token");
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }catch (Exception e){
  16. e.printStackTrace();
  17. }
  18. log.info(Thread.currentThread().getName() + " end token ===> {}", token);
  19. });
  20. return "token=" + RequestUtil.getRequest().getHeader("token");
  21. }

四、总结

        1、在写异步线程代码时,一定要注意异常情况的捕获和处理;若未正确的捕获或处理异常,会导致程序没有达到预期的执行结果,且没有任何异常输出,造成出现问题,难以排查的情况。

        1.1、未正确的处理异常情况:

  1. public static void main(String[] args) {
  2. ExecutorService executor = Executors.newSingleThreadExecutor();
  3. executor.submit(()->{
  4. try {
  5. Thread.sleep(1000);
  6. /**
  7. * 未正确的捕获异常:
  8. * InterruptedException 无法捕获 xx 异常
  9. */
  10. int a = 3/ 0;
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. System.out.println(Thread.currentThread().getName()+" ===> 异步线程执行结束!");
  15. });
  16. System.out.println("main 执行结束 ");
  17. }

        1.2、输出结果:

main 执行结束

        1.3、未正确的捕获异常:

  1. public static void main(String[] args) {
  2. ExecutorService executor = Executors.newSingleThreadExecutor();
  3. executor.submit(()->{
  4. try {
  5. Thread.sleep(1000);
  6. /**
  7. * 未正确的捕获异常:
  8. * InterruptedException 无法捕获 xx 异常
  9. */
  10. int a = 3/ 0;
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. System.out.println(Thread.currentThread().getName()+" ===> 异步线程执行结束!");
  15. });
  16. System.out.println("main 执行结束 ");
  17. }

        2、正确的处理异常情况: 最后一级增加 Exception 捕获

  1. public static void main(String[] args) {
  2. ExecutorService executor = Executors.newSingleThreadExecutor();
  3. executor.submit(()->{
  4. try {
  5. Thread.sleep(1000);
  6. /**
  7. * 未正确的捕获异常:
  8. * InterruptedException 无法捕获 ArithmeticException 异常
  9. */
  10. int a = 3/ 0;
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }catch (Exception e){
  14. e.printStackTrace();
  15. }
  16. System.out.println(Thread.currentThread().getName()+" ===> 异步线程执行结束!");
  17. });
  18. System.out.println("main 执行结束 ");
  19. }

        2.1、输出结果:

  1. main 执行结束
  2. java.lang.ArithmeticException: / by zero
  3. at com.runcode.springboottourist.RequController.lambda$main$3(RequController.java:154)
  4. at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
  5. at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  6. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
  7. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
  8. at java.lang.Thread.run(Thread.java:745)
  9. pool-1-thread-1 ===> 异步线程执行结束!

        3、本文仅仅是解决:异步线程中无法获取request对象的问题;对于主线程结束后,异步线程获取到的 request 对象,会存在request 对象获取到的方法参数都为空的情况,例如:

  1. // 主线程可以正常获取到参数, 异步线程中获取到的是null
  2. RequestUtil.getRequest().getHeader("token");

        建议解决办法: 主线程中获取到,以参数形式传递到子线程、存到redis中、重写request对象等方法进行尝试解决。

        4、RequestContextHolder 方法的实现,点进去源码进行查看,里面有2个 ThreadLocal 对象,是可以解决 父子线程,变量共享的问题,请自行研究。


了解更多: 

        SpringMVC中静态获取request对象 Spring中获取 HttpServletRequest对象 SpringBoot中静态获取request_HaHa_Sir的博客-CSDN博客_静态获取request



​​​​​​​https://blog.csdn.net/HaHa_Sir/article/details/127044832
 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/259719
推荐阅读
相关标签
  

闽ICP备14008679号