如何使用 spring integraton java DSL 实现 enricher?

How to implement enricher using spring integraton java DSL?

我想使用 java DSL

重写 following xml sample

xml 配置:

    <int:channel id="findUserServiceChannel"/>
    <int:channel id="findUserByUsernameServiceChannel"/>

    <!-- See also:
        https://docs.spring.io/spring-integration/reference/htmlsingle/#gateway-proxy
        https://www.enterpriseintegrationpatterns.com/MessagingGateway.html -->
    <int:gateway id="userGateway" default-request-timeout="5000"
                 default-reply-timeout="5000"
                 service-interface="org.springframework.integration.samples.enricher.service.UserService">
        <int:method name="findUser"                  request-channel="findUserEnricherChannel"/>
        <int:method name="findUserByUsername"        request-channel="findUserByUsernameEnricherChannel"/>
        <int:method name="findUserWithUsernameInMap" request-channel="findUserWithMapEnricherChannel"/>
    </int:gateway>

    <int:enricher id="findUserEnricher"
                  input-channel="findUserEnricherChannel"
                  request-channel="findUserServiceChannel">
        <int:property name="email"    expression="payload.email"/>
        <int:property name="password" expression="payload.password"/>
    </int:enricher>

    <int:enricher id="findUserByUsernameEnricher"
                  input-channel="findUserByUsernameEnricherChannel"
                  request-channel="findUserByUsernameServiceChannel"
                  request-payload-expression="payload.username">
        <int:property name="email"    expression="payload.email"/>
        <int:property name="password" expression="payload.password"/>
    </int:enricher>

    <int:enricher id="findUserWithMapEnricher"
                  input-channel="findUserWithMapEnricherChannel"
                  request-channel="findUserByUsernameServiceChannel"
                  request-payload-expression="payload.username">
        <int:property name="user"    expression="payload"/>
    </int:enricher>

    <int:service-activator id="findUserServiceActivator"
                           ref="systemService" method="findUser"
                           input-channel="findUserServiceChannel"/>

    <int:service-activator id="findUserByUsernameServiceActivator"
                           ref="systemService" method="findUserByUsername"
                           input-channel="findUserByUsernameServiceChannel"/>

    <bean id="systemService"
          class="org.springframework.integration.samples.enricher.service.impl.SystemService"/>

现在我有以下内容:

配置:

@Configuration
@EnableIntegration
@IntegrationComponentScan
public class Config {

    @Bean
    public SystemService systemService() {
        return new SystemService();
    }

    @Bean
    public IntegrationFlow findUserEnricherFlow(SystemService systemService) {
        return IntegrationFlows.from("findUserEnricherChannel")
                .<User>handle((p, h) -> systemService.findUser(p))
                .get();
    }

    @Bean
    public IntegrationFlow findUserByUsernameEnricherFlow(SystemService systemService) {
        return IntegrationFlows.from("findUserByUsernameEnricherChannel")
                .<User>handle((p, h) -> systemService.findUserByUsername(p.getUsername()))
                .get();
    }

    @Bean
    public IntegrationFlow findUserWithUsernameInMapFlow(SystemService systemService) {
        return IntegrationFlows.from("findUserWithMapEnricherChannel")
                .<Map<String, Object>>handle((p, h) -> {
                    User user = systemService.findUserByUsername((String) p.get("username"));
                    Map<String, Object> map = new HashMap<>();
                    map.put("username", user.getUsername());
                    map.put("email", user.getEmail());
                    map.put("password", user.getPassword());
                    return map;
                })
                .get();
}
}

服务接口:

@MessagingGateway
public interface UserService {

    /**
     * Retrieves a user based on the provided user. User object is routed to the
     * "findUserEnricherChannel" channel.
     */
    @Gateway(requestChannel = "findUserEnricherChannel")
    User findUser(User user);

    /**
     * Retrieves a user based on the provided user. User object is routed to the
     * "findUserByUsernameEnricherChannel" channel.
     */
    @Gateway(requestChannel = "findUserByUsernameEnricherChannel")
    User findUserByUsername(User user);

    /**
     * Retrieves a user based on the provided username that is provided as a Map
     * entry using the mapkey 'username'. Map object is routed to the
     * "findUserWithMapChannel" channel.
     */
    @Gateway(requestChannel = "findUserWithMapEnricherChannel")
    Map<String, Object> findUserWithUsernameInMap(Map<String, Object> userdata);

}

目标服务:

public class SystemService {

    public User findUser(User user) {
            ...
    }

    public User findUserByUsername(String username) {
            ...    
    }

}

主要方法:

public static void main(String[] args) {
    ConfigurableApplicationContext ctx = new SpringApplication(MyApplication.class).run(args);
    UserService userService = ctx.getBean(UserService.class);
    User user = new User("some_name", null, null);
    System.out.println("Main:" + userService.findUser(user));
    System.out.println("Main:" + userService.findUserByUsername(user));
    Map<String, Object> map = new HashMap<>();
    map.put("username", "vasya");
    System.out.println("Main:" + userService.findUserWithUsernameInMap(map));
}

输出:

2019-08-30 14:09:29.956  INFO 12392 --- [           main] enricher.MyApplication                   : Started MyApplication in 2.614 seconds (JVM running for 3.826)
2019-08-30 14:09:29.966  INFO 12392 --- [           main] enricher.SystemService                   : Calling method 'findUser' with parameter User{username='some_name', password='null', email='null'}
Main:User{username='some_name', password='secret', email='some_name@springintegration.org'}
2019-08-30 14:09:29.967  INFO 12392 --- [           main] enricher.SystemService                   : Calling method 'findUserByUsername' with parameter: some_name
Main:User{username='some_name', password='secret', email='some_name@springintegration.org'}
2019-08-30 14:09:29.967  INFO 12392 --- [           main] enricher.SystemService                   : Calling method 'findUserByUsername' with parameter: vasya
Main:{password=secret, email=vasya@springintegration.org, username=vasya}

如您所见,一切正常,但我在配置内部进行了转换。我不确定我是否必须这样做,因为 xml 配置没有这样的转换,并且一切都以某种方式使用内部魔法工作。这是正确的方法还是我应该使用一些内部 DSL 魔法来进行转换?

P.S.

我想 Config class 可以以某种方式简化。我的意思是 findUserByUsernameEnricherFlow findUserWithUsernameInMapFlow 方法

更新

我意识到我并不真正了解 XML 配置的工作原理:

让我们考虑方法Userservice#findUserWithUsernameInMap方法

它有以下界面:

Map<String, Object> findUserWithUsernameInMap(Map<String, Object> userdata);

它最终会调用 SystemServicefindUserByUsername 方法:

public User findUserByUsername(String username) 

因为客户端代码与 Userservice 一起工作,所以内部有 2 个转换:

  1. 在途中(在 SystemService#findUserByUsername 调用之前)因为 Userservice#findUserWithUsernameInMap接受 Map<String, Object>SystemService#findUserByUsername 接受 String

  2. 正在返回(在 SystemService#findUserByUsername 调用之后)因为 SystemService#findUserByUsernamereturns 用户但是 Userservice#findUserWithUsernameInMap returns Map<String, Object>

这些转换在 xml 配置中的确切位置声明?

我有一个建议,request-payload-expression 用于进行 TO 转换。看起来它可以使用与 Object 相同的方式与 Map 一起工作。但是BACK变换一点都不清楚。当然配置有

<int:property name="user"    expression="payload"/>

但是我不知道这是什么意思。

<int:enricher> 的 Java DSL 等价物是 .enrich()。所以,你的 findUserEnricherFlow 应该是这样的:

@Bean
public IntegrationFlow findUserEnricherFlow(SystemService systemService) {
    return IntegrationFlows.from("findUserEnricherChannel")
            .enrich((enricher) -> enricher
                         .requestChannel("findUserServiceChannel")
                         .propertyExpression("email", "payload.email")
                         .propertyExpression("password", "payload.password"))

            .get();
}

您仍然可以将您的问题简单地指向一种网关方法和一种增强器...

最终我能够将 xml 重写为 java DSL。不幸的是,它有点冗长:

配置:

@Configuration
@EnableIntegration
@IntegrationComponentScan
public class Config {

    @Bean
    public SystemService systemService() {
        return new SystemService();
    }

    //first flow
    @Bean
    public IntegrationFlow findUserEnricherFlow() {
        return IntegrationFlows.from("findUserEnricherChannel")
                .enrich(enricherSpec ->
                        enricherSpec.requestChannel("findUserServiceChannel")
                                .<User>propertyFunction("email", (message) ->
                                        (message.getPayload()).getEmail()
                                ).<User>propertyFunction("password", (message) ->
                                (message.getPayload()).getPassword()
                        ))
                .get();
    }

    @Bean
    public IntegrationFlow findUserServiceFlow(SystemService systemService) {
        return IntegrationFlows.
                from("findUserServiceChannel")
                .<User>handle((p, h) -> systemService.findUser(p))
                .get();
    }

    //second flow
    @Bean
    public IntegrationFlow findUserByUsernameEnricherFlow() {
        return IntegrationFlows.from("findUserByUsernameEnricherChannel")
                .enrich(enricherSpec ->
                        enricherSpec.requestChannel("findUserByUsernameRequestChannel")
                                .<User>requestPayload(userMessage -> userMessage.getPayload().getUsername())
                                .<User>propertyFunction("email", (message) ->
                                        (message.getPayload()).getEmail()
                                ).<User>propertyFunction("password", (message) ->
                                (message.getPayload()).getPassword()
                        ))
                .get();

    }

    @Bean
    public IntegrationFlow findUserByUsernameServiceFlow(SystemService systemService) {
        return IntegrationFlows.from("findUserByUsernameRequestChannel")
                .<String>handle((p, h) -> systemService.findUserByUsername(p))
                .get();
    }

    //third flow
    @Bean
    public IntegrationFlow findUserWithUsernameInMapEnricherFlow() {
        return IntegrationFlows.from("findUserWithMapEnricherChannel")
                .enrich(enricherSpec ->
                        enricherSpec.requestChannel("findUserWithMapRequestChannel")
                                .<Map<String, User>>requestPayload(userMessage -> userMessage.getPayload().get("username"))
                                .<User>propertyFunction("user", Message::getPayload)
                ).get();
    }

    @Bean
    public IntegrationFlow findUserWithUsernameInMapServiceFlow(SystemService systemService) {
        return IntegrationFlows.from("findUserWithMapRequestChannel")
                .<String>handle((p, h) -> systemService.findUserByUsername(p))
                .get();
    }
}

我还在 UserService:

中添加了一些注释
@MessagingGateway
public interface UserService {

    /**
     * Retrieves a user based on the provided user. User object is routed to the
     * "findUserEnricherChannel" channel.
     */
    @Gateway(requestChannel = "findUserEnricherChannel")
    User findUser(User user);

    /**
     * Retrieves a user based on the provided user. User object is routed to the
     * "findUserByUsernameEnricherChannel" channel.
     */
    @Gateway(requestChannel = "findUserByUsernameEnricherChannel")
    User findUserByUsername(User user);

    /**
     * Retrieves a user based on the provided username that is provided as a Map
     * entry using the mapkey 'username'. Map object is routed to the
     * "findUserWithMapChannel" channel.
     */
    @Gateway(requestChannel = "findUserWithMapEnricherChannel")
    Map<String, Object> findUserWithUsernameInMap(Map<String, Object> userdata);

}

所有资源都可以在这里找到:https://github.com/gredwhite/spring-integration/tree/master/complete/src/main/java/enricher


我还发现:

@Bean
public IntegrationFlow findUserEnricherFlow(SystemService systemService) {
    return IntegrationFlows.from("findUserEnricherChannel")
            .enrich(enricherSpec ->
                    enricherSpec//.requestChannel("findUserServiceChannel")
                            .<User>propertyFunction("email", (message) ->
                                    (message.getPayload()).getEmail()
                            ).<User>propertyFunction("password", (message) ->
                            (message.getPayload()).getPassword()
                    ))
            .get();
}

@Bean
public IntegrationFlow findUserServiceFlow(SystemService systemService) {
    return IntegrationFlows.
            from("findUserServiceChannel")
            .<User>handle((p, h) -> systemService.findUser(p))
            .get();
}

可以更简洁地重写:

@Bean
public IntegrationFlow findUserEnricherFlow(SystemService systemService) {
    return IntegrationFlows.from("findUserEnricherChannel")
            .enrich(enricherSpec ->
                    enricherSpec//.requestChannel("findUserServiceChannel")
                            .<User>propertyFunction("email", (message) ->
                                    (message.getPayload()).getEmail()
                            ).<User>propertyFunction("password", (message) ->
                            (message.getPayload()).getPassword()
                    ))
            .<User>handle((p, h) -> systemService.findUser(p))
            .get();
}

还有一个选项:

@Bean
public IntegrationFlow findUserEnricherFlow(SystemService systemService) {
    return IntegrationFlows.from("findUserEnricherChannel")
            .enrich(enricherSpec ->
                    enricherSpec.requestSubFlow(flow -> flow.<User>handle((p, h) -> systemService.findUser(p))
                    ).<User>propertyFunction("email", (message) ->
                            (message.getPayload()).getEmail()
                    ).<User>propertyFunction("password", (message) ->
                            (message.getPayload()).getPassword()
                    ))
            .get();
}