问题描述:
在开发过程中,发现使用@Async修饰的方法,一旦使用到@RequestScope修饰的对象时,就会出现异常。
大致意思是当前线程没有request scope。
搜索过程:
在搜索问题的过程中,发现似乎所有的线索都在指向:How to enable request scope in async task executor
帖子描述问题和我碰到的问题一模一样。既然找到同病相怜的人,那就看怎么解决的吧。于是:
public class ContextAwarePoolExecutor extends ThreadPoolTaskExecutor {
@Override
public <T> Future<T> submit(Callable<T> task) {
return super.submit(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
return super.submitListenable(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));
}
}
public class ContextAwareCallable<T> implements Callable<T> {
private Callable<T> task;
private RequestAttributes context;
public ContextAwareCallable(Callable<T> task, RequestAttributes context) {
this.task = task;
this.context = context;
}
@Override
public T call() throws Exception {
if (context != null) {
RequestContextHolder.setRequestAttributes(context);
}
try {
return task.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
@Configuration
public class ExecutorConfig extends AsyncConfigurerSupport {
@Override
@Bean
public Executor getAsyncExecutor() {
return new ContextAwarePoolExecutor();
}
}
__ 结果并不可行,似乎是因为版本更新,这个方法已经行不通了。 __
随后不甘心的点击RequestContextHolder
里面的代码查看。
发现RequestContextHodler里有两个方法:
public static void setRequestAttributes(RequestAttributes attributes) {
setRequestAttributes(attributes, false);
}
public static void setRequestAttributes(RequestAttributes attributes, boolean inheritable) {
if (attributes == null) {
resetRequestAttributes();
}
else {
if (inheritable) {
inheritableRequestAttributesHolder.set(attributes);
requestAttributesHolder.remove();
}
else {
requestAttributesHolder.set(attributes);
inheritableRequestAttributesHolder.remove();
}
}
}
可以看到,其中setRequestAttributes(RequestAttributes attributes, boolean inheritable)是取不一样的AttributesHolder.
于是我尝试将代码改成
public class ContextAwareCallable<T> implements Callable<T> {
private Callable<T> task;
private RequestAttributes context;
public ContextAwareCallable(Callable<T> task, RequestAttributes context) {
this.task = task;
this.context = context;
}
@Override
public T call() throws Exception {
if (context != null) {
RequestContextHolder.setRequestAttributes(context, true);
}
try {
return task.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
重启测试… …
结果依然是失败的.
之后,我又注意到
try {
return task.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
在finally中调用了RequestContextHolder.resetRequestAttributes();
.
其在RequestContextHolder中对应的源码是:
public static void resetRequestAttributes() {
requestAttributesHolder.remove();
inheritableRequestAttributesHolder.remove();
}
直接将RequestAttributesHolder给清了. 怒从心头起,恶向胆边生,不清了!
将之注释掉.
public class ContextAwareCallable<T> implements Callable<T> {
private Callable<T> task;
private RequestAttributes context;
public ContextAwareCallable(Callable<T> task, RequestAttributes context) {
this.task = task;
this.context = context;
}
@Override
public T call() throws Exception {
if (context != null) {
RequestContextHolder.setRequestAttributes(context, true);
}
try {
return task.call();
} finally {
// RequestContextHolder.resetRequestAttributes();
}
}
}
重启测试… …
成了!
最终代码
public class ContextAwarePoolExecutor extends ThreadPoolTaskExecutor {
@Override
public <T> Future<T> submit(Callable<T> task) {
return super.submit(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
return super.submitListenable(new ContextAwareCallable(task, RequestContextHolder.currentRequestAttributes()));
}
}
public class ContextAwareCallable<T> implements Callable<T> {
private Callable<T> task;
private RequestAttributes context;
public ContextAwareCallable(Callable<T> task, RequestAttributes context) {
this.task = task;
this.context = context;
}
@Override
public T call() throws Exception {
if (context != null) {
RequestContextHolder.setRequestAttributes(context, true);
}
try {
return task.call();
} finally {
// RequestContextHolder.resetRequestAttributes();
}
}
}
@Configuration
public class ExecutorConfig extends AsyncConfigurerSupport {
@Override
@Bean
public Executor getAsyncExecutor() {
return new ContextAwarePoolExecutor();
}
}
__ 该解决过程仅解决了@Async注释的方法调用到了@RequestScope修饰的对象.并没有测试过是否会出现其他问题.仅保存为自己复习所用. __
2019年8月10日13:01:00
最近老大看了一下spring文档,跟我说了一下我最后注释的那个reset方法,感觉还是要放开. 目前还没有改动,等到实际改了再更新.