问题
I want to rewrite following xml sample using java DSL
xml config:
<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"/>
For now I have following:
config:
@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();
}
}
service interface:
@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);
}
and target service:
public class SystemService {
public User findUser(User user) {
...
}
public User findUserByUsername(String username) {
...
}
}
main method:
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));
}
output:
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}
As you can see everything is working properly but I do transformations inside the configuration. I am not sure if I have to do it because xml configuration dooesn't have such transformations and everything somehow works using internal magic. Is it correct way or should I use some internal DSL magic for transformations?
P.S.
I suppose that Config
class can be simplified somehow. I mean findUserByUsernameEnricherFlow
findUserWithUsernameInMapFlow
methods
update
I realized that I don't really understand how the XML config works:
Let's consider method Userservice#findUserWithUsernameInMap
method
It has following interface:
Map<String, Object> findUserWithUsernameInMap(Map<String, Object> userdata);
And it eventually invokes findUserByUsername
method of SystemService
:
public User findUserByUsername(String username)
Because client code work with Userservice
there are 2 transformations inside:
on way TO (before
SystemService#findUserByUsername
invocation) becauseUserservice#findUserWithUsernameInMap
acceptMap<String, Object>
butSystemService#findUserByUsername
accepts StringOn way BACK(after
SystemService#findUserByUsername
invocation) becauseSystemService#findUserByUsername
returns User butUserservice#findUserWithUsernameInMap
returnsMap<String, Object>
Where exactly these transformations are declared in the xml configuration?
I have a suggestion that request-payload-expression is ised to make TO tranformation. Looks like it can work with Map using the same manner as with Object. But BACK transformation is not clear at all. Sure configiration has
<int:property name="user" expression="payload"/>
But I have no idea what does it mean.
回答1:
The Java DSL equivalent of the <int:enricher>
is .enrich()
. so, your findUserEnricherFlow
should be like this:
@Bean
public IntegrationFlow findUserEnricherFlow(SystemService systemService) {
return IntegrationFlows.from("findUserEnricherChannel")
.enrich((enricher) -> enricher
.requestChannel("findUserServiceChannel")
.propertyExpression("email", "payload.email")
.propertyExpression("password", "payload.password"))
.get();
}
You still could simply your question pointing only to one gateway method and one enricher...
回答2:
Eventually I was able to rewrite xml to java DSL. Unfortunately it is a bit more verbose:
config:
@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();
}
}
Also I added a bit annotations in the 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);
}
All sources can be found here: https://github.com/gredwhite/spring-integration/tree/master/complete/src/main/java/enricher
Also I discovered that:
@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();
}
can be rewritten in more concise manner:
@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();
}
one more option:
@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();
}
来源:https://stackoverflow.com/questions/57727592/how-to-implement-enricher-using-spring-integraton-java-dsl