Skip to content
Advertisement

spring-jms – listener exchange and bind queue by jms configuration

I have a project with spring-jms

I’m trying work with exchanges. I created 4 listeners and all of them are been bound into exchange named ‘jms.durable.queues’ by default. Even thought I create my own exchanges and binding my queues manually on rabbit console, spring is creating a default exchange.

How could I create my own queues and bind into my exchanges or disable spring to create queues and bind into default exchange ‘jms.durable.queues’?

My configuration class

import javax.jms.ConnectionFactory;

import com.rabbitmq.jms.admin.RMQConnectionFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;

@EnableJms
@Configuration
public class ConnectionQueueConfig {

    @Bean
    public ConnectionFactory jmsConnectionRabbitFactory(@Autowired RabbitProperties rabbitProperties) {
        RMQConnectionFactory connectionFactory = new RMQConnectionFactory();
        connectionFactory.setUsername(rabbitProperties.getUser());
        connectionFactory.setPassword(rabbitProperties.getPass());
        connectionFactory.setVirtualHost(rabbitProperties.getVirtualhost());
        connectionFactory.setHost(rabbitProperties.getHost());
        connectionFactory.setPort(rabbitProperties.getPort());
        return connectionFactory;
    }

    @Bean
    public DefaultJmsListenerContainerFactory jmsListenerContainerFactory( @Autowired ConnectionFactory connectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setAutoStartup(true);
        return factory;
    }

    @Bean
    public JmsTemplate defaultJmsTemplate(@Autowired ConnectionFactory connectionFactory) {
        return new JmsTemplate(connectionFactory);
    }


}

My listeners

    @JmsListener(destination = "queue-1-from-exchange-A" )
    public void messageConsumer1(@Payload Message message,  @Headers MessageHeaders headers){
    }

    @JmsListener(destination = "queue-2-from-exchange-A" )
    public void messageConsumer2(@Payload Message message,  @Headers MessageHeaders headers){
    }

    @JmsListener(destination = "queue-1-from-exchange-B" )
    public void messageConsumer3(@Payload Message message,  @Headers MessageHeaders headers){
    }

    @JmsListener(destination = "queue-2-from-exchange-B" )
    public void messageConsumer4(@Payload Message message,  @Headers MessageHeaders headers){
    }

dependencies

    implementation 'org.springframework:spring-jms:5.3.13'
    implementation 'com.rabbitmq.jms:rabbitmq-jms:2.3.0'

I have seen about RMQDestination.class, can I use it to create my two exchange and queues? Is there any spring resolver to manager destination programatically on spring-jms configuration?

example as pseudo-code

someSpringResolverDestination.setDestination(new RMQDestination());
someSpringResolverDestination.setDestination(new RMQDestination());

Advertisement

Answer

See source code of this class in RabbitMQ JMS Client: https://github.com/rabbitmq/rabbitmq-jms-client/blob/main/src/main/java/com/rabbitmq/jms/admin/RMQDestination.java.

Since you don’t provide an exchange explicitly that queue name is really bound to that default exchange.

See more docs about this JMS client and how to declare destinations manually for specific exchange and binding : https://www.rabbitmq.com/jms-client.html.

UPDATE

See that documentation closer: https://www.rabbitmq.com/jms-client.html#destination-interoperability

@Bean
public Destination jmsDestination() {
    RMQDestination jmsDestination = new RMQDestination();
    jmsDestination.setDestinationName("myQueue");
    jmsDestination.setAmqp(true);
    jmsDestination.setAmqpQueueName("rabbitQueueName");
    return jmsDestination;
}

Then look into @JmsListener JavaDocs:

/**
 * The destination name for this listener, resolved through the container-wide
 * {@link org.springframework.jms.support.destination.DestinationResolver} strategy.
 */
String destination();

The JmsDestinationAccessor uses a DynamicDestinationResolver by default, which, probably, is not going to work over here for us since it is going to do whatever you have so far with that jms.durable.queues exchange.

So, another option to consider for your solution is the custom:

/**
 * The bean name of the {@link org.springframework.jms.config.JmsListenerContainerFactory}
 * to use to create the message listener container responsible for serving this endpoint.
 * <p>If not specified, the default container factory is used, if any.
 */
String containerFactory() default "";

So, you need to add a bean for the DefaultJmsListenerContainerFactory and inject into its setDestinationResolver(DestinationResolver) a BeanFactoryDestinationResolver. Then you provide a RMQDestination bean name into that destination option of your @JmsListener method and BeanFactory is going to give you a proper RMQDestination with desired exchange and binding.

Advertisement