[英]How to make ThreadPoolTaskExecutor access request scoped bean in Spring Boot
我有要单独处理的项目列表。 所以,我想使用 Java 并行 stream 但线程无法访问我的请求范围 bean。
我尝试了以下实现:
@Bean(name = "myExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("my-thread");
return executor;
}
在我的服务层,我自动装配了执行器:
@Autowired
ThreadPoolTaskExecutor taskExecutor;
public void addOrUpdate(List<ShareholderBalanceCsvItem> itemList) {
// ...
itemList.forEach(csvItem -> taskExecutor.submit(() -> {
log.info(Thread.currentThread().getName() + " ----------------------");
// ...
createCertificateShareEntry(processType, csvItem, certificateShareEntryDTO);
// ...
}));
}
在createCertificateShareEntry
方法中,我需要访问请求 scope 中的MessageBean 。
@Autowired
private MessageBean messageBean;
private void createCertificateShareEntry(String processType, ShareholderBalanceCsvItem csvItem, CertificateShareEntryDTO certificateShareEntryDTO) {
// ...
certificateShareEntryDTO.setMessageOid(messageBean.getObjId());
// ...
}
MessageBean.java
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MessageBean {
// ...
}
我在messageBean.getObjId()
行中收到以下异常
Error creating bean with name 'scopedTarget.messageBean': Scope 'request' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: Cannot ask for request attribute - request is not active anymore!
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:383)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.aop.target.SimpleBeanTargetSource.getTarget(SimpleBeanTargetSource.java:35)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:675)
at tr.com.mkk.hpks.model.MessageBean$$EnhancerBySpringCGLIB$$1db10d79.getObjId(<generated>)
at tr.com.mkk.hpks.service.impl.ShareholderBalanceServiceImpl.createCertificateShareEntry(ShareholderBalanceServiceImpl.java:400)
at tr.com.mkk.hpks.service.impl.ShareholderBalanceServiceImpl.lambda$addOrUpdate$4(ShareholderBalanceServiceImpl.java:314)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:264)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.IllegalStateException: Cannot ask for request attribute - request is not active anymore!
at org.springframework.web.context.request.ServletRequestAttributes.getAttribute(ServletRequestAttributes.java:149)
at org.springframework.web.context.request.AbstractRequestAttributesScope.get(AbstractRequestAttributesScope.java:43)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:371)
... 12 common frames omitted
您需要使用 Context Aware 实现 Runnable 接口,如下所示。 .
public class ContextAwareRunnable implements Runnable {
final Runnable task;
private final RequestAttributes context;
ContextAwareRunnable(Runnable task, RequestAttributes context) {
this.task = task;
this.context = context;
}
@Override
public void run() {
if (context != null) {
RequestContextHolder.setRequestAttributes(context, true);
}
if you have any other Spring/Hystrix managed request object then you should
wire those as well here.
try {
task.run();
} finally {
RequestContextHolder.resetRequestAttributes();
close any other object you initialize in the run method.
}
}
}
enter code here
如果您使用的是 Java 并发 package,那么您还需要 Context Aware Callable ,如下所示。 .
public class ContextAwareCallable<T> implements Callable<T> {
private final Callable<T> task;
private final RequestAttributes context;
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);
}
if you have any other Spring/Hystrix managed request object then you should
wire those as well here.
try {
return task.call();
} finally {
RequestContextHolder.resetRequestAttributes();
close any other object you initialize in the call method.
}
}
}
然后你需要一个上下文感知任务执行器,如下所示。 .
public class ContextAwareThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
@Override
public void execute(Runnable task) {
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
super.execute(new ContextAwareRunnable(task, requestAttributes));
}
@Override
public <T> Future<T> submit(Callable<T> task) {
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
return super.submit(new ContextAwareCallable<>(task, requestAttributes));
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
return super.submitListenable(new ContextAwareCallable<>(task, requestAttributes));
}
}
然后您定义了一个自定义线程池执行器 bean,如下所示
@Bean("contextAwareThreadPoolExecutor")
public Executor contextAwareThreadPoolExecutor() {
ThreadPoolTaskExecutor executor = new ContextAwareThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(25);
executor.setThreadNamePrefix("my-context-aware-thread");
executor.initialize();
return executor;
}
在您的服务层中。 .
@Autowired
@Qualifier("contextAwareThreadPoolExecutor")
Executor executorService;
这个 shd 为你工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.