Flowable+Spring Boot:启动流程后立即返回响应,解决异步任务异常
解决Flowable启动流程后立即返回+后台执行任务的方案
你遇到的问题核心是异步任务脱离Web请求上下文导致Spring Bean依赖报错,同时需要实现流程启动后立刻返回processId、后续任务后台执行的需求。下面是一步步的解决思路和具体方案:
一、核心配置:让首个任务异步执行
要实现启动流程后立即返回,关键是让启动事件之后的第一个任务进入Flowable的异步执行队列,而不是在当前请求线程中同步执行。
在BPMN XML里,给第一个服务任务添加flowable:async="true"属性:
<process id="yourProcessId" name="Your Process"> <startEvent id="startEvent" /> <sequenceFlow sourceRef="startEvent" targetRef="firstAsyncTask" /> <!-- 第一个任务标记为异步,流程启动后会直接返回,任务交给Flowable异步执行器处理 --> <serviceTask id="firstAsyncTask" name="First Background Task" flowable:expression="${yourTaskBean.handleFirstTask(execution)}" flowable:async="true" /> <!-- 后续任务可按需继续标记异步,或保持同步(但都会在后台线程执行) --> <sequenceFlow sourceRef="firstAsyncTask" targetRef="secondTask" /> <serviceTask id="secondTask" name="Second Task" flowable:expression="${yourTaskBean.handleSecondTask(execution)}" /> <sequenceFlow sourceRef="secondTask" targetRef="endEvent" /> <endEvent id="endEvent" /> </process>
同时确保Flowable的异步执行器已启用(Spring Boot环境下默认开启,也可显式配置):
flowable: async-executor-activate: true async-executor: core-pool-size: 5 # 根据业务调整线程池大小 max-pool-size: 10
二、解决Spring Bean的请求上下文异常
你遇到的No thread-bound request found错误,是因为异步任务在Flowable的作业线程中执行,而该线程没有Web请求上下文(RequestAttributes)。解决方式分两种场景:
场景1:可以修改Bean的依赖逻辑(推荐)
尽量避免在异步任务的Bean中直接依赖HttpServletRequest或RequestContextHolder,而是通过流程变量传递所需的请求数据:
- 在启动流程的REST接口中,提取请求里需要的参数,作为流程变量传入:
@PostMapping("/start-process") public ResponseEntity<String> startProcess(HttpServletRequest request) { // 从当前请求中提取需要的数据 String userId = request.getParameter("userId"); String requestParam = request.getHeader("X-Custom-Param"); // 封装为流程变量 Map<String, Object> variables = new HashMap<>(); variables.put("userId", userId); variables.put("customParam", requestParam); // 启动流程,立即返回processId ProcessInstance processInstance = runtimeService.startProcessInstanceByMessage( "yourMessageName", variables ); return ResponseEntity.ok(processInstance.getId()); }
- 在你的任务Bean中,从
DelegateExecution获取流程变量,不再依赖请求上下文:
@Component public class YourTaskBean { // 这里可以正常注入Spring Bean,只要不依赖请求上下文 @Autowired private SomeBusinessService businessService; public void handleFirstTask(DelegateExecution execution) { // 从流程变量中获取数据 String userId = (String) execution.getVariable("userId"); String customParam = (String) execution.getVariable("customParam"); // 执行业务逻辑 businessService.doSomething(userId, customParam); } }
场景2:必须保留请求上下文依赖(不推荐,但可实现)
如果无法修改Bean的逻辑,必须依赖请求上下文,可以手动保存并恢复请求属性:
- 启动流程时,将当前请求的
RequestAttributes存入流程变量:
@PostMapping("/start-process") public ResponseEntity<String> startProcess() { // 保存当前请求上下文 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); Map<String, Object> variables = new HashMap<>(); variables.put("requestAttributes", requestAttributes); ProcessInstance processInstance = runtimeService.startProcessInstanceByMessage( "yourMessageName", variables ); return ResponseEntity.ok(processInstance.getId()); }
- 在任务Bean中,先恢复请求上下文,执行完后再清除:
@Component public class YourTaskBean { @Autowired private HttpServletRequest request; // 现在可以正常使用了 public void handleFirstTask(DelegateExecution execution) { RequestAttributes requestAttributes = (RequestAttributes) execution.getVariable("requestAttributes"); try { // 恢复请求上下文 RequestContextHolder.setRequestAttributes(requestAttributes); // 执行依赖请求的逻辑 String userId = request.getParameter("userId"); // ...业务处理 } finally { // 必须清除,避免污染后续复用的线程 RequestContextHolder.resetRequestAttributes(); } } }
三、验证效果
启动流程后,你的REST接口会在流程启动事件完成后立刻返回processId,而第一个标记为async="true"的任务会被Flowable的异步执行器调度到后台线程执行,后续任务也会在后台依次运行,完全不阻塞客户端请求。
内容的提问来源于stack exchange,提问作者cwiq
相关产品推荐
相关产品推荐

