# 错误处理

## 错误处理

正如本手册开头的[overview](./overview.html#overview)中所描述的， Spring 集成之类的面向消息的框架背后的主要动机之一是促进组件之间的松耦合。消息渠道起着重要的作用，因为生产者和消费者不必相互了解。然而，这些优点也有一些缺点。在松散耦合的环境中，有些事情会变得更加复杂，其中一个例子就是错误处理。

当将消息发送到通道时，最终处理该消息的组件可能在与发送方相同的线程中运行，也可能不在相同的线程中运行。如果使用一个简单的默认`DirectChannel`（当`<channel>`元素没有`<queue>`子元素且没有“task-executor”属性时），消息处理发生在发送初始消息的同一个线程中。在这种情况下，如果抛出了一个`Exception`，它可以被发送方捕获（或者如果它是一个未捕获的`RuntimeException`，它可能会传播通过发送方）。这与普通 爪哇 调用堆栈中的抛出异常操作具有相同的行为。

在调用者线程上运行的消息流可以通过消息网关（参见[消息传递网关](./gateway.html#gateway)）或`MessagingTemplate`（参见[`MessagingTemplate`]（./channel.html#channel-template））调用。在这两种情况下，默认的行为都是向调用方抛出任何异常。有关消息传递网关，请参见[错误处理](./gateway.html#gateway-error-handling)，以了解如何引发异常以及如何配置网关将错误路由到错误通道的详细信息。当使用`MessagingTemplate`或直接发送到`MessageChannel`时，异常总是被抛给调用方。

当添加异步处理时，事情会变得相当复杂。例如，如果“channel”元素确实提供了一个“queue”子元素（在 Java&Annotations 配置中是`QueueChannel`），那么处理消息的组件在与发送方不同的线程中进行操作。当使用`ExecutorChannel`时也是如此。发送者可能已经将`Message`放入通道中，然后转移到其他东西上。通过使用标准的`Exception`抛出技术，无法将`Exception`直接抛回给发送方。相反，处理异步进程的错误要求错误处理机制也是异步的。

Spring 集成支持通过将错误发布到消息通道来对其组件进行错误处理。具体地说，`Exception`将成为 Spring 集成`ErrorMessage`的有效负载。然后将`Message`发送到消息通道，该消息通道的解析方式类似于“replychannel”解析。首先，如果在`Exception`发生时正在处理的请求`Message`包含一个“errorchannel”头（头名在`MessageHeaders.ERROR_CHANNEL`常量中定义），则`ErrorMessage`被发送到该通道。否则，错误处理程序将发送到一个“全局”通道，该通道的 Bean 名称是`errorChannel`（这也被定义为一个常数：`IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME`）。

默认的`errorChannel` Bean 是由框架在内部创建的。但是，如果你想要控制设置，则可以定义自己的设置。下面的示例展示了如何在 XML 配置中定义错误通道，该配置由容量`500`的队列支持：

Java

```
@Bean
QueueChannel errorChannel() {
    return new QueueChannel(500);
}
```

XML

```
<int:channel id="errorChannel">
    <int:queue capacity="500"/>
</int:channel>
```

|   |默认的错误通道是`PublishSubscribeChannel`。|
|---|---------------------------------------------------------|

这里需要理解的最重要的一点是，基于消息传递的错误处理仅适用于在`TaskExecutor`中执行的 Spring 集成任务引发的异常。这不适用于在与发送方相同的线程中操作的处理程序引发的异常（例如，通过`DirectChannel`，如本节前面所述）。

|   |当计划的 Poller 任务执行中出现异常时，这些异常被包装在`ErrorMessage`实例中，并发送到“errorchannel”。<br/>这是通过将`MessagePublishingErrorHandler`注入全局`taskScheduler` Bean 中来完成的。<br/>对于任何自定义`MessagePublishingErrorHandler`，都建议使用`MessagePublishingErrorHandler`。如果错误处理仍然必须使用标准的“errorchannel”集成流逻辑来完成。<br/>在这种情况下可以使用已注册的`integrationMessagePublishingErrorHandler` Bean。|
|---| |

要启用全局错误处理，请在该通道上注册一个处理程序。例如，你可以将 Spring Integration 的`ErrorMessageExceptionTypeRouter`配置为订阅到“errorchannel”的端点的处理程序。然后，该路由器可以基于`Exception`类型，在多个通道上传播错误消息。

从版本 4.3.10 开始， Spring 集成提供了`ErrorMessagePublisher`和`ErrorMessageStrategy`。你可以将它们用作发布`ErrorMessage`实例的通用机制。你可以在任何错误处理场景中调用或扩展它们。`ErrorMessageSendingRecoverer`将这个类扩展为`RecoveryCallback`实现，该实现可用于重试，例如[`RequestHandlerRetryAdvice`]（./handler-advice.html#retry-advice）。`ErrorMessageStrategy`用于基于提供的异常和`AttributeAccessor`上下文构建`ErrorMessage`。它可以被注入任何`MessageProducerSupport`或`MessagingGatewaySupport`。`requestMessage`存储在`ErrorMessageUtils.INPUT_MESSAGE_CONTEXT_KEY`上下文中。`ErrorMessageStrategy`可以使用`requestMessage`作为它创建的`ErrorMessage`属性的`originalMessage`。`DefaultErrorMessageStrategy`就是这么做的。

从版本 5.2 开始，所有由框架组件抛出的`MessageHandlingException`实例中，包含一个组件`BeanDefinition`的资源和源，以确定一个配置点的异常形式。在 XML 配置的情况下，资源是一个 XML 文件路径，并源具有`id`属性的 XML 标记。对于 Java&Annotation 配置，资源是`@Configuration`类，而源是`@Bean`方法。在大多数情况下，目标集成流解决方案是基于开箱即用的组件及其配置选项的。当运行时发生异常时，堆栈跟踪中不涉及任何最终用户代码，因为执行是针对 bean 的，而不是针对它们的配置。 Bean 包括资源和源的定义有助于确定可能的配置错误并提供更好的开发人员体验。

从版本 5.4.3 开始，默认的错误通道配置为属性`requireSubscribers = true`，当此通道上没有订阅服务器时（例如，当应用程序上下文停止时），不会静默忽略消息。在这种情况下，将抛出一个`MessageDispatchingException`，它可能会在入站通道适配器的客户端回调中负地确认（或回滚）源系统中的原始消息，以用于重新交付或其他将来的考虑。要恢复以前的行为（忽略未分派的错误消息），全局集成属性`spring.integration.channels.error.requireSubscribers`必须设置为`false`。有关更多信息，请参见[全局属性](./configuration.html#global-properties)和[`PublishSubscribeChannel`配置]（./channel.html#channel-configuration-pubsubchannel）（如果你手动配置全局`errorChannel`）。