SpringBoot--解决子线程无法获得HttpServletRequest的attribute的问题

原文网址:SpringBoot--解决子线程无法获得HttpServletRequest的attribute的问题_IT利刃出鞘的博客-CSDN博客

简介

        本文介绍解决SpringBoot子线程无法获得HttpServletRequest的attribute的问题。

        在SpringBoot请求中,如果创建了子线程获取request的attribute,会无法获取到。比如:我想记录日志,将日志放到了request的attribute中:request.setAttribute("logContent", "日志内容"),然后创建子线程去获取logContent,然后进行处理。

问题复现

代码

package com.knife.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

@RestController
public class HelloController {

    @GetMapping("/test")
    public String test() {
        ServletRequestAttributes servletRequestAttributes =
                (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        request.setAttribute("logContent", "日志内容:" + LocalDateTime.now());

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                ServletRequestAttributes servletRequestAttributes =
                        (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = servletRequestAttributes.getRequest();

                System.out.println("子线程运行。获取到的attribute为:"
                        + request.getAttribute("logContent"));
            }
        });
        thread.start();

        return "test success";
    }
}

结果(getRequestAttributes返回null)

Exception in thread "Thread-131" java.lang.NullPointerException
	at com.knife.controller.HelloController$1.run(HelloController.java:26)
	at java.lang.Thread.run(Thread.java:748)

解决方案

创建子线程之前调用如下代码:

RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);

代码

package com.knife.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

@RestController
public class HelloController {

    @GetMapping("/test")
    public String test() {
        ServletRequestAttributes servletRequestAttributes =
                (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        request.setAttribute("logContent", "日志内容:" + LocalDateTime.now());

        RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                ServletRequestAttributes servletRequestAttributes =
                        (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = servletRequestAttributes.getRequest();

                System.out.println("子线程运行。获取到的attribute为:"
                        + request.getAttribute("logContent"));
            }
        });
        thread.start();

        return "test success";
    }
}

结果(可以获取到属性)

子线程运行。获取到的attribute为:日志内容:2022-09-03T16:43:18.463

线程安全性

        上边的解决方案是否有线程安全问题呢?我在子线程里延时5秒模拟长时间请求,然后多次请求,看结果。如果值都是不一样的话,应该是线程安全的。(本处我无法百分百确定,实际开发中不太建议这么写。实际开发中建议将日志内容作为线程的参数传入,与attribute剥离开)。

代码

package com.knife.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

@RestController
public class HelloController {

    @GetMapping("/test")
    public String test() {
        ServletRequestAttributes servletRequestAttributes =
                (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        request.setAttribute("logContent", "日志内容:" + LocalDateTime.now());

        RequestContextHolder.setRequestAttributes(servletRequestAttributes, true);

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                ServletRequestAttributes servletRequestAttributes =
                        (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
                HttpServletRequest request = servletRequestAttributes.getRequest();

                System.out.println("子线程运行。获取到的attribute为:"
                        + request.getAttribute("logContent"));

                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        thread.start();

        return "test success";
    }
}

结果(日志内容是新的,应该是线程安全的)

子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:42.242
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:42.799
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:43.359
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:43.877
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:44.392
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:44.804
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:45.247
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:45.610
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:46.010
子线程运行。获取到的attribute为:日志内容:2022-09-03T16:46:46.433

其他网址

RequestContextHolder.getRequestAttributes()子线程获取问题_ypp91zr的博客-CSDN博客

spring boot开启异步导致RequestContextHolder.currentRequestAttributes()为NULL_ERD Online的博客-CSDN博客

猜你喜欢

转载自blog.csdn.net/feiying0canglang/article/details/126646326