我有一些解决方案,大致分为Callable(for @Async)、AsyncExecutionInterceptor(for @Async)、CallableProcessingInterceptor(for controller)。
1.将上下文信息放入@Async线程的Callable解决方案:
关键是使用ContextAwarePoolExecutor来替换@Async的默认执行器:
@Configuration
公共类 DemoExecutorConfig {
@Bean("demoExecutor")
public Executor contextAwarePoolExecutor() {
return new ContextAwarePoolExecutor();
}
}
而 ContextAwarePoolExecutor 使用 ContextAwareCallable 覆盖 submit 和 submitListenable 方法:
public class ContextAwarePoolExecutor extends ThreadPoolTaskExecutor {
private static final long serialVersionUID = 667815067287186086L;
@Override
public <T> Future<T> submit(Callable<T> task) {
return super.submit(new ContextAwareCallable<T>(task, newThreadContextContainer()));
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
return super.submitListenable(new ContextAwareCallable<T>(task, newThreadContextContainer()));
}
/**
* set infos what we need
*/
private ThreadContextContainer newThreadContextContainer() {
ThreadContextContainer container = new ThreadContextContainer();
container.setRequestAttributes(RequestContextHolder.currentRequestAttributes());
container.setContextMapOfMDC(MDC.getCopyOfContextMap());
return container;
}
}
ThreadContextContainer 只是一个方便存储信息的 pojo:
public class ThreadContextContainer implements Serializable {
private static final long serialVersionUID = -6809291915300091330L;
private RequestAttributes requestAttributes;
private Map<String, String> contextMapOfMDC;
public RequestAttributes getRequestAttributes() {
return requestAttributes;
}
public Map<String, String> getContextMapOfMDC() {
return contextMapOfMDC;
}
public void setRequestAttributes(RequestAttributes requestAttributes) {
this.requestAttributes = requestAttributes;
}
public void setContextMapOfMDC(Map<String, String> contextMapOfMDC) {
this.contextMapOfMDC = contextMapOfMDC;
}
}
ContextAwareCallable(原始任务的可调用代理)在原始任务执行其调用方法之前覆盖调用方法以存储 MDC 或其他上下文信息:
public class ContextAwareCallable<T> implements Callable<T> {
/**
* the original task
*/
private Callable<T> task;
/**
* for storing infos what we need
*/
private ThreadContextContainer threadContextContainer;
public ContextAwareCallable(Callable<T> task, ThreadContextContainer threadContextContainer) {
this.task = task;
this.threadContextContainer = threadContextContainer;
}
@Override
public T call() throws Exception {
// set infos
if (threadContextContainer != null) {
RequestAttributes requestAttributes = threadContextContainer.getRequestAttributes();
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes);
}
Map<String, String> contextMapOfMDC = threadContextContainer.getContextMapOfMDC();
if (contextMapOfMDC != null) {
MDC.setContextMap(contextMapOfMDC);
}
}
try {
// execute the original task
return task.call();
} finally {
// clear infos after task completed
RequestContextHolder.resetRequestAttributes();
try {
MDC.clear();
} finally {
}
}
}
}
最后,像这样使用@Async 和配置的bean“demoExecutor”:@Async("demoExecutor")
void yourTaskMethod();
2.关于您处理回复的问题:
遗憾地告诉我,我真的没有经过验证的解决方案。也许 org.springframework.aop.interceptor.AsyncExecutionInterceptor#invoke 可以解决这个问题。
而且我认为它没有解决方案来处理您的 ServletLoggingFilter 的响应。因为 Async 方法会立即返回。 afterRequest 方法立即执行并在 Async 方法执行操作之前返回。除非您同步等待 Async 方法完成执行,否则您不会得到您想要的。
但如果你只是想记录一些东西,你可以在原始任务执行其调用方法之后将这些代码添加到我的示例 ContextAwareCallable 中:
try {
// execute the original task
return task.call();
} finally {
String something = MDC.get("doSomething"); // will not be null
// logthis(something);
// clear infos after task completed
RequestContextHolder.resetRequestAttributes();
try {
MDC.clear();
} finally {
}
}