问题
I got a JmsConfig
configuration class that handles JMS events from a topic in the following way:
- It defines a
@Bean ConnectionFactory
, containing an ActiveMQ implementation - It defines a
@Bean JmsListenerContainerFactory
instantiating aDefaultJmsListenerContainerFactory
and passing it through Boot'sDefaultJmsListenerContainerFactoryConfigurer
- It defines a
@Bean MessageConverter
containing aMappingJackson2MessageConverter
and setting a customObjectMapper
- I use
@JmsListener
annotation pointing to myfactory on a method of my service. This is the only use I have for the topic, subscription alone.
Now I want to move to Spring Integration. After reading a lot, and provided I don't need a bidirectional use (discarding Gateways) neither a polling mechanism (discarding @InboundChannelAdapter
), I am going for a message-driven-channel-adapter
, in traditional XML configuration wording. I found that Java idiom should be accomplished by means of the new Spring Integration DSL library, and thus, I look for the proper snippet.
It seems JmsMessageDrivenChannelAdapter
is the proper equivalent, and I found a way:
IntegrationFlows.from(Jms.messageDriverChannelAdapter(...))
But the problem is that this only accepts the ActiveMQ ConnectionFactory or an AbstractMessageListenerContainer
, but no my boot pre-configured JmsListenerContainerFactory
!
How should this be implemented in an ultimate way?
回答1:
JmsListenerContainerFactory
is specific for the @JmsListener
, it's a higher level abstraction used to configure a DefaultMessageListenerContainer
. Boot does not provide an auto configuration option for a raw DefaultMessageListenerContainer
; you have to wire it up yourself. But you can still use the Boot properties...
@Bean
public IntegrationFlow flow(ConnectionFactory connectionFactory,
JmsProperties properties) {
return IntegrationFlows.from(Jms.messageDrivenChannelAdapter(container(connectionFactory, properties)))
...
.get();
}
private DefaultMessageListenerContainer container(ConnectionFactory connectionFactory,
JmsProperties properties) {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConcurrentConsumers(properties.getListener().getConcurrency());
container.setMaxConcurrentConsumers(properties.getListener().getMaxConcurrency());
...
return container;
}
回答2:
There is even a better approach. I am surprised Gary did not comment it.
There's an out-of-the-box builder called Jms.container(...)
.
@Bean
public IntegrationFlow jmsMyServiceMsgInboundFlow(ConnectionFactory connectionFactory, MessageConverter jmsMessageConverter, MyService myService, JmsProperties jmsProperties, @Value("${mycompany.jms.destination.my-topic}") String topicDestination){
JmsProperties.Listener jmsInProps = jmsProperties.getListener();
return IntegrationFlows.from(
Jms.messageDrivenChannelAdapter( Jms.container(connectionFactory, topicDestination)
.pubSubDomain(false)
.sessionAcknowledgeMode(jmsInProps .getAcknowledgeMode().getMode())
.maxMessagesPerTask(1)
.errorHandler(e -> e.printStackTrace())
.cacheLevel(0)
.concurrency(jmsInProps.formatConcurrency())
.taskExecutor(Executors.newCachedThreadPool())
.get()))
)
.extractPayload(true)
.jmsMessageConverter(jmsMessageConverter)
.destination(topicDestination)
.autoStartup(true)
//.errorChannel("NOPE")
)
.log(LoggingHandler.Level.DEBUG)
.log()
.handle(myService, "myMethod", e -> e.async(true).advice(retryAdvice()))
.get();
来源:https://stackoverflow.com/questions/44887651/proper-ultimate-way-to-migrate-jms-event-listening-to-spring-integration-with-sp