开启子线程,jpa无法获取request的当前用户

起因

在开发过程中,因为业务逻辑比较复杂,需要异步返回,所以需要开启子线程去操作

这些操作中是需要保存数据的,而恰好用的是jpa,并且有@CreatedBy注解,保存数据时,会去找到实现 AuditorAware接口的类,去获取当前操作的用户名。

但是因为开启了多线程,原本使用的方法是通过下面的方法获取,现在获取的request是null了

((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()

分析

在求知欲(铁打的工作)的不断探索(百度)下,发现是因为开启子线程后,不享有主线程的request导致的。因此现在就需要想办法让子线程也 可以拿到request里面的当前用户信息。

解决方法

第一种方法:RequestAttributes设置为子线程共享

这是搜索下答案最多一个,就是下面这个方法,放在开启子线程前就好了(但是看有人说主线程销毁了,子线程就获取不到了,后续有时间再细细研究,也欢迎研究过的大佬指点指点)

ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
RequestContextHolder.setRequestAttributes(sra, true);

第二种方法:使用ThreadLocal

在寻找的过程中,我发现了ThreadLocal,然后还发现ThreadLocal的set方法如下:

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

可以看出,set方法会和当前线程绑定。

这样就可以利用ThreadLocal的set的值会和线程关联起来的性质,先把需要使用的用户信息从主线程获取,并用一个变量去接收

接着在开启子线程后,把这个变量的值set到ThreadLocal对象里。在需要用的地方直接调用就可以拿到。

jpa使用@CreatedBy注解时,需要实现的类

@Component
public class AuditorAwareImpl implements AuditorAware<String> {
    @Override
    public Optional<String> getCurrentAuditor() {

        String loginUser = ContextHolder.getCurrentUser();

        return Optional.of(loginUser);

    }
}

获取当前操作用户的类

public class ContextHolder {
    //在此定义一个ThreadLocal对象去接收子线程的用户信息
    private static ThreadLocal<String> tl = new ThreadLocal<>();

    private ContextHolder() {
    }

    //获取request
    public static HttpServletRequest getRequest() {
        return RequestContextHolder.getRequestAttributes() == null ? null: ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    //调用该方法设置
    public static void setLoginUser(String loginUser){
        tl.set(loginUser);
    }

    //结束任务后将该子线程的信息移除
    public static void removeLoginUser(){
        tl.remove();
    }

    public static String getCurrentUser(){
        HttpServletRequest request = getRequest();
        //判断如果request为空,则去找子线程的操作用户信息
        return request == null ? tl.get() : request.getHeader("loginUser");
    }

}

示例代码:

public class testThreadLocal() {
    //先从主线程获取当前用户信息
    String loginUser = ContextHolder.getCurrentUser();
    new Thread(() -> {
        ContextHolder.setLoginUser(loginUser);
        //do something
        ContextHolder.removeLoginUser();
    });
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值