簡體   English   中英

如何在 Spring 引導中使 ThreadPoolTaskExecutor 訪問請求范圍 bean

[英]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);
           // ...

        }));
}

我看到為列表中的 4 個項目正確生成了 4 個線程在此處輸入圖像描述

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM