菜单

Administrator
Administrator
发布于 2023-12-12 / 51 阅读 / 0 评论 / 0 点赞

Async注解引发的空指针问题

现象:利用Async注解异步调用 ,在异步调用方法过程中获取请求上下文报空指针。

原因分析:RequestContextHolder内部采用ThreadLocal(不支持继承),切换线程时无法获取到请求信息。

解决方案:

  1. 实现上下文复制装饰器 org.springframework.core.task.TaskDecorator


import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.concurrent.Callable;

/**
 * @author xietao
 * @since 2023/12/13 19:24
 */
public class ContextCopyCallableDecorator<V> implements Callable<V> {
    private Callable<V> delegate;
    private RequestAttributes context;

    public ContextCopyCallableDecorator(Callable<V> delegate) {
        this.delegate = delegate;
        this.context = RequestContextHolder.getRequestAttributes();
    }


    @Override
    public V call() throws Exception {
        try {
            RequestContextHolder.setRequestAttributes(context);
            return delegate.call();
        } finally {
            RequestContextHolder.resetRequestAttributes();
        }
    }
}
  1. 实现自定义Executor

import org.springframework.core.task.SimpleAsyncTaskExecutor;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;

/**
 * @author xietao
 * @since 2023/12/13 19:22
 */
public class ContextCopyExecutor extends SimpleAsyncTaskExecutor {
    @Override
    public <T> Future<T> submit(Callable<T> task) {
        return super.submit(new ContextCopyCallableDecorator<>(task));
    }
}
  1. 继承org.springframework.scheduling.annotation.AsyncConfigurer

import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Component;

import java.util.concurrent.Executor;

@Component
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    public AsyncConfig() {
    }

    @Override
    public Executor getAsyncExecutor() {
        return new ContextCopyExecutor();
    }

}

问题点:

异步线程如果在执行前睡眠一段时间后在获取请求上下文,会由于浅拷贝导致一系列问题。

解决方案:

  1. 深拷贝请求

  2. 业务上避免这种问题


评论