Activiti7流程结束监听事件中,抛出的异常无法被spring全局异常捕捉

52

ProcessRuntimeEventListener

activiti7中,提供了ProcessRuntimeEventListener监听器,用于监听流程实例的结束事件

/**
 * 流程完成监听器
 */
@Slf4j
@Component
public class ProcessCompleteListener implements ProcessRuntimeEventListener<ProcessCompletedEvent> {

    @Override
    public void onEvent(ProcessCompletedEvent event) {
        // ...处理自己的业务逻辑

        // 这里写一段抛出异常的测试代码
        int a = 1 / 0;
    }
}

上述代码中,由于1/0会抛出运行时异常,理论上来说应该被我们的全局异常所捕获

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 通用未知异常
     */
    @ExceptionHandler(Throwable.class)
    @ResponseStatus(HttpStatus.OK)
    public ResultEntity<?> error(Exception e) {
        return ResultEntity.fail(ResultEnum.SERVER_ERROR, "系统错误,请联系管理员!");
    }

}

实际情况是无法捕获

解决思路

既然异常没有被一层一层的抛出去直到被全局异常捕获,那说明调用ProcessCompleteListener.onEvent()的某个地方使用try catch将异常捕获了该异常并没有继续向上抛出,随着这个思路,我们将断点打在1/0这行代码上面

9e424658998d21de11a9255c92c262bd.png
然后重新运行代码,让代码执行到断点位置
027875e02c5994d6ab72d9504fb374b0.png

然后在左侧的方法调用栈中,一个一个找,是哪个地方调用了onEvent并且进行了try catch异常捕捉
8e016877010ccf7341494c98bff554c7.png
找到使用了try catch的实际的调用处,如图所示
catch中通过判断listener的isFailOnException()来控制是否抛出异常

由断点可以看出,listener对象是ProcessCompletedListenerDelegate
d19c61181df13f4e6cc0647ae53a6fe8.png
044d14b8ae81ace00d2593c81660b865.png
由此可见isFailOnException总是返回false,所以一直不会将异常抛出

解决办法

我们可以创建一个CustomProcessCompletedListenerDelegate自定义子类继承该类,然后重新isFailOnException方法,但是问题是如何将spring环境中的ProcessCompletedListenerDelegate替换成我们的自定义子类
那么我们就看ProcessCompletedListenerDelegate类是什么时候创建出来的,在创建时,替换成我们自定义的类

有2个办法:

  1. ProcessCompletedListenerDelegate类的构造器上面打断点(小提示:如果遇到想给构造器打断点,但是没有写构造器的情况下,就在类名所在行打断点),然后重启项目,等执行到断点行的时候,再次查看方法调用栈,找到什么时候创建的该类
  2. 直接alt + F7,或者右键类名点Find Usages查找使用地方

1544bf7a8d7861ee9814f9ede8f6d5a3.png

取巧一点的方法是先用方式2,看看使用的地方多不多,如果很多无法确定具体使用的地方,那么在用方法1

这里可以直接使用方法2,找到调用处
a78af62771e10b0bf3332d9cb58d0ef6.png

ProcessRunTimeAutoConfiguration的369行在使用,直接点进去

21b3af6b96a57e95f94c706fc2589346.png

这就好办了,看到了@ConditionOnMissingBean,那么我们可以直接自己注入CustomProcessCompletedListenerDelegate到Spring中来管理就可以

解决代码如下

自定义监听器委托类CustomProcessCompletedListenerDelegate

/**
 * 继承ProcessCompletedListenerDelegate,重写isFailOnException以达目的
 */
public class CustomProcessCompletedListenerDelegate extends ProcessCompletedListenerDelegate {
    public CustomProcessCompletedListenerDelegate(List<ProcessRuntimeEventListener<ProcessCompletedEvent>> processRuntimeEventListeners, ToProcessCompletedConverter processCompletedConverter) {
        super(processRuntimeEventListeners, processCompletedConverter);
    }

    @Override
    public boolean isFailOnException() {
        return true;
    }
}

注入Spring,我这里使用的是SpringBoot

@Configuration
// 如果是spi方式注入,则添加如下这行控制顺序
// @AutoConfigureBefore(ProcessRuntimeAutoConfiguration.class)
public class ProcessCompleteListenerConfig {

    @Bean("registerProcessCompletedListenerDelegate")
    public InitializingBean registerProcessCompletedListenerDelegate(
            RuntimeService runtimeService,
            @Autowired(required = false) List<ProcessRuntimeEventListener<ProcessCompletedEvent>> eventListeners,
            ToProcessCompletedConverter converter) {
        return () -> runtimeService.addEventListener(new CustomProcessCompletedListenerDelegate(getInitializedListeners(eventListeners),
                        converter),
                ActivitiEventType.PROCESS_COMPLETED);
    }

    private <T> List<T> getInitializedListeners(List<T> eventListeners) {
        return eventListeners != null ? eventListeners : Collections.emptyList();
    }


}