简体   繁体   English

在Java Spring Integration中,转换器元素可以包括路由器功能吗?

[英]In Java Spring Integration, can a transformer element include router functionality?

I need a component that: 我需要一个组件:

  1. Receives a message, enacts a transformation on both the payload and header of the message (so far it acts like a transformer). 接收一条消息,对消息的有效负载和标头进行转换(到目前为止,它的作用就像一个转换器)。

  2. Then, based on the values passed in on the header route to an appropriate channel (acting like a header-router). 然后,基于在标头路由上传递到适当通道的值(充当标头路由器)。

I'd like to configure this purely using annotations in java rather than XML, but I'll absolutely take what I can get in that regard. 我只想使用Java中的注释而不是XML来配置它,但是我绝对会考虑到这一点。 If someone knows the XML solution please pass it along. 如果有人知道XML解决方案,请传递它。

If it helps, my use case is that I want the transformation that the transformer enacts on a message payload to be dependent on a custom loaded header value. 如果有帮助,我的用例是我希望转换器对消息有效负载执行的转换依赖于自定义加载的标头值。 I also want the channel that is used to sent the message from the transformer to be dependent on the header value. 我还希望用于从变压器发送消息的通道取决于标头值。

I am aware of the solution that involves multiple transformers, one per transformation type as defined in the header value, and then a router. 我知道该解决方案涉及多个转换器,每个转换类型(在标头值中定义)一个,然后一个路由器。 I'm trying to avoid this solution and only use at most a single router and single transformer. 我试图避免这种解决方案,并且最多只能使用一个路由器和一个变压器。

Thank you for your help. 谢谢您的帮助。

In Spring Integration channels act as any other beans. 在Spring Integration中,渠道就像其他任何bean一样。 You could use a service activator to invoke a method on any bean. 您可以使用服务激活器在任何bean上调用方法。 That bean could have the required channels injected. 该bean可以注入所需的通道。 You could use the @Qualifier annotation to select, which channel should be injected, or just autowire a Map<String, MessageChannel> wich would get indexed by the bean name of the channel. 您可以使用@Qualifier批注来选择应该注入哪个通道,或者只是自动将Map<String, MessageChannel>夹在其中,然后通过该通道的Bean名称进行索引。 It could pass transformed messages to those channels. 它可以将转换后的消息传递到那些通道。

A Spring Boot app: Spring Boot应用程序:

package demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
import org.springframework.integration.config.EnableIntegration;

@SpringBootApplication
@EnableIntegration
@ImportResource("classpath:int.xml")
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }


}

The gateway interface: 网关接口:

package demo;

public interface MyGateway {
    public void send(Object o);
}

The service to be invoked: 要调用的服务:

package demo;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.stereotype.Service;

@Service
public class MyService {

    private final Map<String, MessageChannel> channels;

    @Autowired
    public MyService(Map<String, MessageChannel> channels) {
        super();
        this.channels = channels;
    }

    public void transformAndRoute(Message<?> in) {
        // do your business logic here
        Message<?> out = MessageBuilder.fromMessage(in).build();

        // if(something)...
        channels.get("fooChannel").send(out);
    }

}

Integration config: 集成配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:int="http://www.springframework.org/schema/integration"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    http://www.springframework.org/schema/integration/spring-integration.xsd">

    <int:gateway id="myGateway" service-interface="demo.MyGateway"
        default-request-channel="inChannel" />

    <int:service-activator input-channel="inChannel"
        ref="myService" method="transformAndRoute" />

    <int:channel id="inChannel" />


    <int:logging-channel-adapter id="fooChannel" level="INFO" log-full-message="true"/>

</beans>

And finally a simple integration test: 最后是一个简单的集成测试:

package demo;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=DemoApplication.class)
public class MyGatewayIT {

    @Autowired
    MyGateway myGateway;

    @Test
    public void test() {
        myGateway.send(new Object());

        // do your assertions here
    }

}

Output: 输出:

14:17:19.733 [main] DEBUG o.s.i.handler.LoggingHandler - org.springframework.integration.handler.LoggingHandler#0 received message: GenericMessage [payload=java.lang.Object@6f2cfcc2, headers={id=6791344c-07b4-d420-0d17-e2344f4bf15b, timestamp=1437826639733}]

BUT

The main benefit of developing message based systems is that you create your application out of small, loosely coupled components that are easy to test, reuse and change. 开发基于消息的系统的主要好处是,您可以使用易于测试,重用和更改的小型,松散耦合的组件创建应用程序。 Creating a component that plays two roles in a process brakes that rule. 创建在流程中扮演两个角色的组件会破坏该规则。 Additionally your code is realy tied to Spring Integration if you create it as above. 另外,如果您按上述方式创建代码,则您的代码确实与Spring Integration相关联。 What you could do instead is create a couple of components that each have a single responsibility, and then configure them to act in a chain. 相反,您可以做的是创建几个组件,每个组件都有一个职责,然后将它们配置为以链状运行。

What you could do is create a transformer that modifies the payload and headers of the message. 您可以做的是创建一个修改消息的有效负载和标头的转换器。 That transformer would encapsulate your business logic and would set a header (say, myRoutingHeader ) that can be later used in routing. 该转换器将封装您的业务逻辑,并设置一个标头(例如myRoutingHeader ),该标头稍后可用于路由。 It would probably be even better to have a transformer for business logic, and a header enricher for adding the header. 最好有一个用于业务逻辑的转换器,以及一个用于添加标头的标头浓缩器。 But let's assume that you are doing it in a single class. 但是,让我们假设您是在一个单独的类中进行操作。 Define that class as a bean (say myTransformer ). 将该类定义为bean(例如myTransformer )。 Then in your config : 然后在您的配置中:

<int:channel id="inChannel/>

<!-- if performance is important consider using a SpEL expression to 
    invoke your method instead as they can be configured to be compiled -->
<int:transformer ref="myTransformer" input-channel="inChannel"
             method="transform" output-channel="routingChannel"/>

<int:channel id="routingChannel/>

<int:header-value-router input-channel="routingChannel" header-name="myRoutingHeader">
    <int:mapping value="foo" channel="fooChannel" />
    <int:mapping value="bar" channel="barChannel" />
</int:header-value-router>

sounds like chaining will do it for you 听起来像链条会帮你

<chain input-chanel="source" output-channel="routing-channel">
 <transform/>
 <!-- any enrichment process or intermediate process can go here -->
</chain>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM