You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ExpressionEvaluatingRequestHandlerAdvice中抛自定义异常并被全局处理器捕获

问题分析

你遇到的核心问题是:Spring Integration分散聚合(scatter-gather)模式下,failure流中抛出的MyCustomException无法被全局异常处理器捕获,但其他场景正常。根源在于:

  1. ExpressionEvaluatingRequestHandlerAdvice开启了setTrapException(true),会将HTTP调用的异常拦截并转发到自定义failure.input通道,阻止了异常向上传播;
  2. flow2使用了异步线程池通道,该线程内抛出的异常会被Spring Integration默认捕获,不会传递到全局异常处理器的上下文;
  3. 分散聚合的gather阶段会收集各分支结果(包括失败通道的返回值),不会主动抛出异常,导致全局处理器无法感知。
解决办法

方法1:利用Spring Integration全局ErrorChannel

将异常转发到框架默认的errorChannel,让全局异常处理器能捕获到:

  1. 修改expressionAdvice配置,关闭异常拦截,让异常直接进入errorChannel:
@Bean
public Advice expressionAdvice() {
    ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice();
    advice.setSuccessChannelName("success.input");
    advice.setOnSuccessExpressionString("payload + ' was successful'");
    // 移除自定义失败通道配置
    advice.setReturnFailureExpressionResult(false);
    advice.setTrapException(false); // 不拦截异常,让其向上传播到errorChannel
    return advice;
}
  1. 配置errorChannel的处理流,在这里转换为自定义异常:
@Bean
public IntegrationFlow errorFlow() {
    return IntegrationFlows.from("errorChannel")
            .handle(message -> {
                Throwable rootCause = ((MessagingException) message.getPayload()).getCause();
                if (rootCause.getMessage().contains("Read timed out")) {
                    throw new MyCustomException(ErrorCode.TIMEOUT_ERROR.getErrorData());
                } else {
                    throw new MyCustomException(ErrorCode.SERVICE_ERROR.getErrorData());
                }
            })
            .get();
}

方法2:聚合后统一检查并抛出异常

保留自定义失败通道逻辑,将异常作为消息 payload 传递回主流程,在聚合后统一抛出:

  1. 修改failure流,返回自定义异常而非直接抛出:
@Bean
public IntegrationFlow failure() {
    return f -> f.handle((p, h) -> {
        if (p.toString().contains("Read timed out")) {
            return new MyCustomException(ErrorCode.TIMEOUT_ERROR.getErrorData());
        } else {
            return new MyCustomException(ErrorCode.SERVICE_ERROR.getErrorData());
        }
    });
}
  1. 在主流程聚合后添加异常检查步骤:
@Bean
public IntegrationFlow mainFlow() {
    return flow ->
        flow.split()
            .channel(c -> c.executor(Executors.newCachedThreadPool()))
            .scatterGather(
                scatterer ->
                    scatterer
                        .applySequence(true)
                        .recipientFlow(flow1())
                        .recipientFlow(flow2()),
                gatherer ->
                    gatherer
                        .releaseLockBeforeSend(true)
                        .releaseStrategy(group -> group.size() == 1))
            .aggregate()
            // 检查聚合结果,抛出异常
            .handle((payload, headers) -> {
                if (payload instanceof List<?> results) {
                    for (Object result : results) {
                        if (result instanceof MyCustomException ex) {
                            throw ex; // 抛出后由全局处理器捕获
                        }
                    }
                }
                return payload;
            })
            .to(anotherFlow());
}

方法3:配置异步线程的异常处理

如果需要在failure流中直接抛出异常,需为异步线程池配置异常处理器,将异常转发到全局上下文:

@Bean
public IntegrationFlow flow2() {
    return flow -> {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
        // 为线程池设置未捕获异常处理器
        executor.setThreadFactory(r -> {
            Thread thread = new Thread(r);
            thread.setUncaughtExceptionHandler((t, e) -> {
                if (e instanceof MyCustomException) {
                    // 调用全局异常处理器的逻辑
                    yourGlobalExceptionHandler.handleException((MyCustomException) e);
                }
            });
            return thread;
        });

        flow.channel(c -> c.executor(executor))
            .handle(
                Http.outboundGateway(
                        "http://localhost:4444/test", dummyService.restTemplate())
                    .httpMethod(HttpMethod.POST)
                    .expectedResponseType(String.class),
                c -> c.advice(expressionAdvice()));
    };
}
关键注意点
  • setTrapException(true)会拦截异常并转发到失败通道,此时异常不会向上传播,全局处理器无法捕获;若需要异常传播,需关闭该配置或手动转发异常。
  • Spring Integration异步线程的异常默认不会关联到Spring MVC全局异常处理器,需通过ErrorChannel或线程异常处理器手动传递。

内容的提问来源于stack exchange,提问作者Somnath Mukherjee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 16:16:03