Most of the resources I have seen online declare MessageChannel
s in the following way:
@Bean
MessageChannel myChannel() {
return new MessageChannels.direct().get();
}
It seems the .get()
method has been deprecated with a note that the framework will get the object when it's time automatically. And some of the newer samples have the message channel bean declared to return the spec instead:
@Bean
DirectChannelSpec myChannel() {
return new MessageChannels.direct();
}
The mychannel()
can still be referenced in most cases since IntegrationFlow.from() allows you to pass in a Spec instead of the actual channel. However, in some cases there's no support for passing in the spec. The one case I'm running into is using .enrichHeaders
to specify an error channel. I can only pass in the MessageChannel itself or the message channel name as a string.
Is it now general practice to just use the string name of the message channel in this case? Or should I instead declare a bean that simply uses the channel's constructor?
@Bean
MessageChannel certainErrorChannel() {
return new DirectChannel();
}
What's the downside of not using the Spec
factories?
When I originally designed the MessageChannels
factory 10 years ago, it was meant for only in-line usage as fluent API for Java DSL configuration. Then I realized that too many method chain levels causes spaghetti impression for the code, so I made that factory an all the ChannelSpec
implementations as public API. And now we have some choice for creating channels: via their constructors or using this factory. Which in some case still causes some confusion, since end-user are facing paradox of choice. Which feels like you have bumped, too.
The enrichHeaders()
and its errorChannel()
configuration is really not the same what is IntegrationFlow.from()
or channel()
. These two has an effect at configuration phase, therefore we really can pass a ChannelSpec
and the framework will configure for us respective beans.
The errorChannel
option of the HeaderEnricher
is already about runtime logic to add such a header into the message passing. And therefore the Spec
cannot be used here since we don't deal with the configuration phase over here.
Passing channel name everywhere instead is very convenient way. First: it make the flow definition where easy readable. Second, if channel does not exit at configuration phase, it is going to be created (if we really talk about configuration components). The channel name is resolved to its bean at runtime.
However as you see it has that flaw that we need to resolve beans from the name at runtime. Especially if you talk about enrichHeaders()
: every message at runtime is going to be modified for that header with channel name. And later on that name will be resolved to bean.
The most efficient way is really to use plain ctor for the channel bean: you have all the control over the object and don't suffer from some missing API in the MessageChannels
factory. The you use a parameter injection into an IntegrationFlow
bean definition and use this MessageChannel
instance whenever you need in the DSL. This way no extra beans created and no extra logic at runtime. Only the problem that more codding.
In the everything is you own choice and conventions.
NOTE: it is recommended to use @Configuration(proxyBeanMethods = false)
and don't use bean method references from each other: no proxy - no extra overhead.
EDIT: declaring an IntegrationFlow with proxy bean methods disable:
@Bean
IntegrationFlow myFlow(MessageChannel myErrorChannel, MessageChannel out) {
IntegrationFlow.from(...)
.enrichHeaders("errorChannel", myErrorChannel)
.handle(...)
.channel(out)
.get();
}
@Bean
public MessageChannel myErrorChannel { return new DirectChannel(); }
@Bean
public MessageChannel out { return new DirectChannel(); }