«1. Обзор

Spring предоставляет среду интеграции JMS, упрощающую использование JMS API. В данной статье представлены основные понятия такой интеграции.

2. Зависимость от Maven

Чтобы использовать Spring JMS в нашем приложении, нам нужно добавить необходимые артефакты в pom.xml:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jms</artifactId>
    <version>4.3.3.RELEASE</version>
</dependency>

Новейшую версию артефакта можно найти здесь.

3. Класс JmsTemplate

JmsTemplate обрабатывает создание и освобождение ресурсов при отправке или синхронном получении сообщений.

Следовательно, класс, который использует этот JmsTemplate, должен только реализовать интерфейсы обратного вызова, как указано в определении метода.

Начиная с Spring 4.1, JmsMessagingTemplate строится поверх JmsTemplate, который обеспечивает интеграцию с абстракцией обмена сообщениями, т. е. org.springframework.messaging.Message. Это, в свою очередь, позволяет нам создать сообщение для отправки в общем виде.

4. Управление подключением

Чтобы подключиться и иметь возможность отправлять/получать сообщения, нам нужно настроить ConnectionFactory.

ConnectionFactory — это один из администрируемых объектов JMS, который предварительно настраивается администратором. Клиент с помощью конфигурации установит соединение с провайдером JMS.

Spring предоставляет 2 типа ConnectionFactory:

    SingleConnectionFactory — это реализация интерфейса ConnectionFactory, которая будет возвращать одно и то же соединение при всех вызовах createConnection() и игнорировать вызовы close() CachingConnectionFactory — расширяет функциональность SingleConnectionFactory и дополняет его кэшированием Sessions, MessageProducers и MessageConsumers

5. Управление местом назначения

Как обсуждалось выше, наряду с ConnectionFactory, места назначения также являются администрируемыми объектами JMS и могут храниться и извлекаться из JNDI.

Spring предоставляет общие преобразователи, такие как DynamicDestinationResolver, и специальные преобразователи, такие как JndiDestinationResolver.

JmsTemplate делегирует разрешение имени назначения одной из реализаций на основе нашего выбора.

Он также предоставит свойство с именем defaultDestination, которое будет использоваться с операциями отправки и получения, не относящимися к конкретному месту назначения.

6. Преобразование сообщений

Spring JMS был бы неполным без поддержки преобразователей сообщений.

Стратегия преобразования по умолчанию, используемая JmsTemplate для операций ConvertAndSend() и ReceiveAndConvert(), — это класс SimpleMessageConverter.

SimpleMessageConverter может обрабатывать сообщения TextMessages, BytesMessages, MapMessages и ObjectMessages. Этот класс реализует интерфейс MessageConverter.

Помимо SimpleMessageConverter, Spring JMS предоставляет некоторые другие классы MessageConverter из коробки, такие как MappingJackson2MessageConverter, MarshallingMessageConverter, MessagingMessageConverter.

Кроме того, мы можем создавать собственные функции преобразования сообщений, просто реализуя методы интерфейса MessageConverter toMessage() и FromMessage().

Давайте посмотрим пример фрагмента кода по реализации пользовательского MessageConverter,

public class SampleMessageConverter implements MessageConverter {
    public Object fromMessage(Message message) 
      throws JMSException, MessageConversionException {
        //...
    }

    public Message toMessage(Object object, Session session)
      throws JMSException, MessageConversionException { 
        //...
    }
}

7. Пример Spring JMS

В этом разделе мы увидим, как использовать JmsTemplate для отправки и получения сообщений.

Метод отправки сообщения по умолчанию — JmsTemplate.send(). Он имеет два ключевых параметра, из которых первый параметр — это место назначения JMS, а второй параметр — реализация MessageCreator. JmsTemplate использует метод обратного вызова MessageCreator createMessage() для создания сообщения.

JmsTemplate.send() хорош для отправки простых текстовых сообщений, но для отправки пользовательских сообщений JmsTemplate имеет другой метод, называемый convertAndSend().

Ниже мы можем увидеть реализацию этих методов:

public class SampleJmsMessageSender {

    private JmsTemplate jmsTemplate;
    private Queue queue;

    // setters for jmsTemplate & queue

    public void simpleSend() {
        jmsTemplate.send(queue, s -> s.createTextMessage("hello queue world"));
    }
    public void sendMessage(Employee employee) { 
        System.out.println("Jms Message Sender : " + employee); 
        Map<String, Object> map = new HashMap<>(); 
        map.put("name", employee.getName()); map.put("age", employee.getAge()); 
        jmsTemplate.convertAndSend(map); 
    }
}

«Ниже приведен класс получателя сообщений, мы называем его POJO, управляемым сообщениями (MDP). Мы видим, что класс SampleListener реализует интерфейс MessageListener и предоставляет текстовую реализацию для метода интерфейса onMessage().

public class SampleListener implements MessageListener {

    public JmsTemplate getJmsTemplate() {
        return getJmsTemplate();
    }

    public void onMessage(Message message) {
        if (message instanceof TextMessage) {
            try {
                String msg = ((TextMessage) message).getText();
                System.out.println("Message has been consumed : " + msg);
            } catch (JMSException ex) {
                throw new RuntimeException(ex);
            }
        } else {
            throw new IllegalArgumentException("Message Error");
        }
    }

    public Employee receiveMessage() throws JMSException {
        Map map = (Map) getJmsTemplate().receiveAndConvert();
        return new Employee((String) map.get("name"), (Integer) map.get("age"));
    }
}

Помимо метода onMessage(), наш класс SampleListener также вызывает метод receiveAndConvert() для получения пользовательских сообщений:

<bean id="messageListener" class="com.baeldung.spring.jms.SampleListener" /> 

<bean id="jmsContainer" 
  class="org.springframework.jms.listener.DefaultMessageListenerContainer"> 
    <property name="connectionFactory" ref="connectionFactory"/> 
    <property name="destinationName" ref="IN_QUEUE"/> 
    <property name="messageListener" ref="messageListener" /> 
</bean>

Мы видели, как реализовать MessageListener, и ниже мы видим конфигурацию в контексте приложения Spring: ~~ ~

DefaultMessageListenerContainer — это контейнер прослушивателя сообщений по умолчанию, который Spring предоставляет вместе со многими другими специализированными контейнерами.

8. Базовая конфигурация с аннотациями Java

@JmsListener — единственная аннотация, необходимая для преобразования метода обычного компонента в конечную точку слушателя JMS. Spring JMS предоставляет гораздо больше аннотаций для упрощения реализации JMS.

@JmsListener(destination = "myDestination")
public void SampleJmsListenerMethod(Message<Order> order) { ... }

Мы можем увидеть некоторые примеры классов, аннотированные ниже:

Чтобы добавить несколько слушателей к одному методу, нам просто нужно добавить несколько аннотаций @JmsListener.

@Configuration
@EnableJms
public class AppConfig {

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

Нам нужно добавить аннотацию @EnableJms в один из наших классов конфигурации для поддержки аннотированных методов @JmsListener:

9. Обработчик ошибок

Мы также можем настроить собственный обработчик ошибок для нашего прослушивателя сообщений контейнер.

@Service
public class SampleJmsErrorHandler implements ErrorHandler {

    // ... logger

    @Override
    public void handleError(Throwable t) {
        LOG.warn("In default jms error handler...");
        LOG.error("Error Message : {}", t.getMessage());
    }

}

Давайте сначала реализуем интерфейс org.springframework.util.ErrorHandler:

Обратите внимание, что мы переопределили метод handleError(), который просто регистрирует сообщение об ошибке.

@Bean
public DefaultJmsListenerContainerFactorybjmsListenerContainerFactory() {
    DefaultJmsListenerContainerFactory factory 
      = new DefaultJmsListenerContainerFactory();
    factory.setConnectionFactory(connectionFactory());
    factory.setErrorHandler(sampleJmsErrorHandler);
    return factory;
}

Затем нам нужно сослаться на нашу службу обработчика ошибок в DefaultJmsListenerConnectionFactory с помощью метода setErrorHandler():

Теперь наш сконфигурированный обработчик ошибок будет ловить любые необработанные исключения и регистрировать сообщение.

<bean id="sampleJmsErrorHandler"
  class="com.baeldung.spring.jms.SampleJmsErrorHandler" />

<bean id="jmsContainer"
  class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="destinationName" value="IN_QUEUE" />
    <property name="messageListener" ref="messageListener" />
    <property name="errorHandler" ref="sampleJmsErrorHandler" />
</bean>

При желании мы также можем настроить обработчик ошибок, используя старые конфигурации XML, обновив наш appContext.xml:

10. Заключение

В этом руководстве мы обсудили настройку и основные концепции Весна JMS. Мы также кратко рассмотрели специфичные для Spring классы JmsTemplate, которые используются для отправки и получения сообщений.