如何在Spring集成流程中处理异常

huangapple go评论72阅读模式
英文:

How to handle Exceptions with a Spring Integration Flow

问题

以下是翻译好的内容:

我正在尝试使用Spring Integration来完成以下操作。

我想从输入通道读取传入的消息,该消息经过一系列步骤并产生最终消息。每个步骤都可能引发异常,这可能是由于数据库调用或外部调用等原因引起的。每个步骤都被视为服务激活器。如果一切顺利,将生成一个成功的最终消息。我需要处理每个步骤引发的每个异常作为单独的情况,然后生成适当的最终消息。

使用下面的XML,我编写了一个测试代码,当成功时可以通过,但当出现错误时,尽管我在通道'finalChannel'的日志中看到生成了最终消息(postSend中的sent=true),但进程不会返回到最终队列。

**为什么会发生这种情况?**

<?xml version="1.0" encoding="UTF-8"?>
<context:component-scan base-package="com.demo."/&gt;
<header-enricher input-channel="inputChannel"
    output-channel="enrichedChannel"&gt;
    <header name="initialMessage" expression="getPayload()"/&gt;
</header-enricher>
<!-- first-step -->
<service-activator input-channel="enrichedChannel"
    output-channel="firstStep" ref="validateOne"/&gt;
<gateway id="validateOne" default-request-channel="ch1"
    error-channel="errors1"/&gt;

<chain input-channel="ch1"&gt;
    <service-activator id="mockServiceOneActivator"
        ref="mockServiceOne" method="register"/&gt;
</chain>
<!-- enrich-header with payload to be available down the line -->
<header-enricher input-channel="firstStep"
    output-channel="secondStep"&gt;
    <header name="initialInfo" expression="getPayload()"/&gt;
</header-enricher>
<!-- second-step -->
<service-activator input-channel="secondStep"
    output-channel="enrichWithTwoChannel" ref="validateTwo"/&gt;
<gateway id="validateTwo" default-request-channel="ch2"
    error-channel="errors2"/&gt;
<chain input-channel="ch2"&gt;
    <service-activator id="mockServiceTwoActivator"
        ref="mockServiceTwo" method="callExternal"/&gt;
</chain>
<!-- enrich-header with payload to be available down the line -->
<header-enricher input-channel="enrichWithTwoChannel"
    output-channel="eligibilityCheck"&gt;
    <header name="serviceTwoInfo" expression="getPayload()"/&gt;
</header-enricher>
<!-- final-step -->
<service-activator input-channel="eligibilityCheck"
    output-channel="finalChannel" id="mockServiceFinalActivator"
    ref="mockServiceFinal" method="submit"/&gt;
<!-- error handling -->
<service-activator input-channel="errors1"
    output-channel="finalChannel" ref="traceErrorHandler"
    method="handleFailedTrace"/&gt;
<service-activator input-channel="errors2"
    output-channel="finalChannel" ref="traceErrorHandler2"
    method="handleFailedTrace"/&gt;

<channel id="finalChannel"&gt;
    <queue/&gt;
</channel>
<service-activator input-channel="errorChannel"
    ref="globalExceptionHandler" method="handleError"/&gt;
我的处理程序代码如下..

@Component("traceErrorHandler")public class TraceErrorHandler {
public Message<FinalMessage> handleFailedTrace(Message<?> errorMessage) {
    MessagingException payload = (MessagingException) errorMessage.getPayload();
    InitialMessage im = (InitialMessage) payload.getFailedMessage().getHeaders().get("initialMessage");
    ServiceOneException error = (ServiceOneException) payload.getCause();
    FinalMessage v = new FinalMessage(im.getFrom(), im.getTo(), error.getErrorTemplate());
    Message<FinalMessage> finalMessage = MessageBuilder.withPayload(v).copyHeaders(payload.getFailedMessage().getHeaders()).build();
    return finalMessage;
}}

我不确定这是否是正确的错误处理方法。最初的链路只是简单的激活器,但在后续的步骤中将会有更多的逻辑。
无论是不是一个单一的激活器服务,我们是否应该始终使用链路来处理单独的错误通道?

1. 即使只是调用一个单一的激活器服务。
2. 只有在有多个服务激活器/转换封装一个单一的错误点时?

**编辑1**
我添加了一个引用了bean的request-handler-advice-chain。

<int:service-activator
    id="serviceOneActivator" input-channel="enrichedChannel"
    ref="serviceOne" method="register" output-channel="firstStep"&gt;
    <int:request-handler-advice-chain>
        <ref bean="myclass"/&gt;
    </int:request-handler-advice-chain></int:service-activator>

引用的bean首先在xml中定义为默认值

<bean id="myclass"
    class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice"&gt;
    <property name="failureChannel" ref="finalChannel"/&gt;
    <property name="onFailureExpression" value="#payload"/&gt;
    <property name="returnFailureExpressionResult" value="true" /&gt;
</bean>

如果我添加`<property name="onFailureExpression" value="#payload"/>`属性,它会失败并显示:

`无法将类型为'java.lang.String'的值转换为所需类型'org.springframework.expression.Expression',用于属性'onFailureExpression'`
我想做的是将异常消息转换为最终消息对象,但我添加的所有表达式似乎都无法加载成功。
英文:

I am trying to do the following using spring integration.

I would like to read from an input channel an incoming Message that goes throws a series of Steps and produces a final message.Each step can throw an Exception. This could be either due to a DB call or ExternalCall etc. Each step is considered as a service-activator. If all is well a successful a final Message is generated.I need to handle each Exception thrown by each Step as a separate case and then produce the appropriate final Message.

Using the below xml I have written a test code that passes when sucessfull but when an error occurs although I see a Final message generated in the postSend (sent=true) on channel 'bean 'finalChannel' log but the process does not return to the final queue.

Why does this happen?

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;context:component-scan base-package=&quot;com.demo.&quot;&gt;
&lt;/context:component-scan&gt;

&lt;header-enricher input-channel=&quot;inputChannel&quot;
	output-channel=&quot;enrichedChannel&quot;&gt;
	&lt;header name=&quot;initialMessage&quot; expression=&quot;getPayload()&quot; /&gt;
&lt;/header-enricher&gt;
&lt;!-- first-step --&gt;
&lt;service-activator input-channel=&quot;enrichedChannel&quot;
	output-channel=&quot;firstStep&quot; ref=&quot;validateOne&quot; /&gt;
&lt;gateway id=&quot;validateOne&quot; default-request-channel=&quot;ch1&quot;
	error-channel=&quot;errors1&quot; /&gt;

&lt;chain input-channel=&quot;ch1&quot;&gt;
	&lt;service-activator id=&quot;mockServiceOneActivator&quot;
		ref=&quot;mockServiceOne&quot; method=&quot;register&quot; /&gt;
&lt;/chain&gt;
&lt;!-- enrich-header with payload to be available down the line --&gt;
&lt;header-enricher input-channel=&quot;firstStep&quot;
	output-channel=&quot;secondStep&quot;&gt;
	&lt;header name=&quot;initialInfo&quot; expression=&quot;getPayload()&quot; /&gt;
&lt;/header-enricher&gt;
&lt;!-- second-step --&gt;
&lt;service-activator input-channel=&quot;secondStep&quot;
	output-channel=&quot;enrichWithTwoChannel&quot; ref=&quot;validateTwo&quot; /&gt;
&lt;gateway id=&quot;validateTwo&quot; default-request-channel=&quot;ch2&quot;
	error-channel=&quot;errors2&quot; /&gt;
&lt;chain input-channel=&quot;ch2&quot;&gt;
	&lt;service-activator id=&quot;mockServiceTwoActivator&quot;
		ref=&quot;mockServiceTwo&quot; method=&quot;callExternal&quot; /&gt;
&lt;/chain&gt;
&lt;!-- enrich-header with payload to be available down the line --&gt;
&lt;header-enricher input-channel=&quot;enrichWithTwoChannel&quot;
	output-channel=&quot;eligibilityCheck&quot;&gt;
	&lt;header name=&quot;serviceTwoInfo&quot; expression=&quot;getPayload()&quot; /&gt;
&lt;/header-enricher&gt;
&lt;!-- final-step --&gt;
&lt;service-activator input-channel=&quot;eligibilityCheck&quot;
	output-channel=&quot;finalChannel&quot; id=&quot;mockServiceFinalActivator&quot;
	ref=&quot;mockServiceFinal&quot; method=&quot;submit&quot; /&gt;
&lt;!-- error handling --&gt;
&lt;service-activator input-channel=&quot;errors1&quot;
	output-channel=&quot;finalChannel&quot; ref=&quot;traceErrorHandler&quot;
	method=&quot;handleFailedTrace&quot; /&gt;
&lt;service-activator input-channel=&quot;errors2&quot;
	output-channel=&quot;finalChannel&quot; ref=&quot;traceErrorHandler2&quot;
	method=&quot;handleFailedTrace&quot; /&gt;

&lt;channel id=&quot;finalChannel&quot;&gt;
	&lt;queue /&gt;
&lt;/channel&gt;
&lt;service-activator input-channel=&quot;errorChannel&quot;
	ref=&quot;globalExceptionHandler&quot; method=&quot;handleError&quot; /&gt;

My handler code looks like this ..

@Component(&quot;traceErrorHandler&quot;)public class TraceErrorHandler {
public Message&lt;FinalMessage&gt; handleFailedTrace(Message&lt;?&gt; errorMessage) {
    MessagingException payload = (MessagingException) errorMessage.getPayload();
    InitialMessage im = (InitialMessage) payload.getFailedMessage().getHeaders().get(&quot;initialMessage&quot;);
    ServiceOneException error = (ServiceOneException) payload.getCause();
    FinalMessage v = new FinalMessage(im.getFrom(), im.getTo(), error.getErrorTemplate());
    Message&lt;FinalMessage&gt; finalMessage = MessageBuilder.withPayload(v).copyHeaders(payload.getFailedMessage().getHeaders()).build();
    return finalMessage;
}}

I am not sure if this is the correct approach in regards to error-handling. The initial chains are simple activators but further down the line there will be more logic.
Should we I always use chains to handle separate error channels

  1. Even if its a single activator service called.
  2. Only when there are Multiple service-activator/transforms that encapsulate a single point of error?

EDIT 1
I added a request-handler-advice-chain reference a bean.

&lt;int:service-activator
	id=&quot;serviceOneActivator&quot; input-channel=&quot;enrichedChannel&quot;
	ref=&quot;serviceOne&quot; method=&quot;register&quot; output-channel=&quot;firstStep&quot;&gt;
	&lt;int:request-handler-advice-chain&gt;
		&lt;ref bean=&quot;myclass&quot; /&gt;
	&lt;/int:request-handler-advice-chain&gt;&lt;/int:service-activator&gt;

The reference bean is at first a default definition in xml

&lt;bean id=&quot;myclass&quot;
	class=&quot;org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice&quot;&gt;
	&lt;property name=&quot;failureChannel&quot; ref=&quot;finalChannel&quot; /&gt;
	&lt;property name=&quot;onFailureExpression&quot; value=&quot;#payload&quot;/&gt;
	&lt;property name=&quot;returnFailureExpressionResult&quot; value=&quot;true&quot; /&gt;
&lt;/bean&gt;

if I add the &lt;property name=&quot;onFailureExpression&quot; value=&quot;#payload&quot;/&gt; property it fails with:

Cannot convert value of type &#39;java.lang.String&#39; to required type &#39;org.springframework.expression.Expression&#39; for property &#39;onFailureExpression&#39;
What i would like to do is convert the exception message to a final message object? but all expression i have added seem to fail on load.

答案1

得分: 0

一个只有一个组件的链没有意义。

链只是一种语法糖,在运行时每个组件仍然是独立的。

如果您想要为单独的端点处理错误,请考虑使用ExpressionEvaluatingRequestHandlerAdvice

请参阅 https://docs.spring.io/spring-integration/docs/5.3.2.RELEASE/reference/html/messaging-endpoints.html#message-handler-advice-chain

英文:

A chain with one component makes no sense.

A chain is syntactic sugar only, each component still stands alone at runtime.

If you want to handle errors for individual endpoints, consider using ExpressionEvaluatingRequestHandlerAdvices instead.

See https://docs.spring.io/spring-integration/docs/5.3.2.RELEASE/reference/html/messaging-endpoints.html#message-handler-advice-chain

huangapple
  • 本文由 发表于 2020年9月8日 21:49:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/63795348.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定