Главная » Spring » Работа с шаблонами JMS Spring

0

 

Класс JmsTemplate – это ответ фреймворка Spring на необходимость писать массу шаблонного кода для работы с JMS. Класс JmsTemplate берет на себя все хлопоты по созданию соединений, открытию се- ансов и приему/передаче сообщений. Он позволяет разработчику сосредоточиться на конструировании сообщений для передачи или обработке принимаемых сообщений.

Более того, JmsTemplate может обрабатывать все эти неудобные ис- ключения JMSException. Если в процессе работы в классе JmsTemplate

будет возбуждено исключение JMSException, оно будет перехвачено, и повторно будет возбуждено неконтролируемое исключение, являю- щееся одним из подклассов класса JmsException.

В табл. 13.1 перечислены неконтролируемые исключения, на- следники JmsException, реализованные в фреймворке Spring, и соот- ветствующие им стандартные исключения, наследники JMSException.

Таблица 13.1. Класс JmsTemplate перехватывает стандартные исключения JMSException и в ответ возбуждает соответствующие им исключения JmsException

Spring   (org.springframework.jms.*)

JMS (javax.jms.*)

DestinationResolutionException

Не имеет аналога в JMS. Возбуждает- ся, когда фреймворк Spring не может определить имя приемника

IllegalStateException

IllegalStateException

InvalidClientIDException

InvalidClientIDException

InvalidDestinationException

InvalidDestinationException

InvalidSelectorException

InvalidSelectorException

JmsSecurityException

JmsSecurityException

ListenerExecutionFailedException

Не имеет аналога в JMS. Возбуждается фреймворком Spring, когда выполнение метода приема завершается неудачей

MessageConversionException

Не имеет аналога в JMS. Возбуждается фреймворком Spring, когда преобразо- вание сообщения завершается неудачей

MessageEOFException

MessageEOFException

MessageFormatException

MessageFormatException

MessageNotReadableException

MessageNotReadableException

MessageNotWriteableException

MessageNotWriteableException

ResourceAllocationException

ResourceAllocationException

SynchedLocalTransactionFailedException

Не имеет аналога в JMS. Возбуждается фреймворком Spring, когда синхрони- зированная локальная транзакция не может быть завершена

TransactionInProgressException

TransactionInProgressException

TransactionRolledBackException

TransactionRolledBackException

UncategorizedJmsException

Не имеет аналога в JMS. Возбуждается фреймворком Spring в ситуациях, когда никакое другое исключение неприме- нимо

К чести JMS API, иерархия JMSException включает богатый набор подклассов, позволяющих определить причины, повлекшие исклю-

чительную ситуацию. Однако все подклассы JMSException являются контролируемыми исключениями и потому обязательно должны перехватываться. Класс JmsTemplate автоматически перехватывает все эти исключения и возбуждает соответствующие им неконтролируе- мые исключения, наследующие класс JmsException.

История о двух версиях класса JmsTemplate. Фактически в состав фреймворка Spring включены два класса шаблонов JMS: JmsTemplate и JmsTemplate102. JmsTemplate102 – это специализированная версия класса JmsTemplate, предназначенная для взаимодействий с провайдерами JMS

1.0.2. В спецификации JMS 1.0.2 темы и очереди интерпретируются со- вершенно иначе – как домены. В спецификации JMS 1.1+ темы и очере- ди объединяются прикладным интерфейсом, не зависящим от доменов. Поскольку темы и очереди в JMS 1.0.2 интерпретируются иначе, для взаимодействия со старыми реализациями JMS был создан специали- зированный класс JmsTemplate102. В этой главе предполагается исполь- зование современного провайдера JMS, поэтому все свое внимание мы сосредоточим исключительно на классе JmsTemplate.

Внедрение шаблона JMS

Чтобы задействовать класс JmsTemplate, необходимо объявить со- ответствующий компонент в конфигурационном файле Spring, как показано ниже:

<bean  id="jmsTemplate"

class="org.springframework.jms.core.JmsTemplate">

<property name="connectionFactory" ref="connectionFactory" />

</bean>

Поскольку экземпляру JmsTemplate нужно знать, как приобрести соединение с брокером сообщений, в свойстве connectionFactory ком- понента следует указать ссылку на компонент, реализующий интер- фейс JMS ConnectionFactory. В примере выше внедряется ссылка на компонент connectionFactory, объявленный выше, в разделе 13.2.1.

Вот и все, что необходимо, чтобы задействовать класс JmsTemplate, – теперь все готово к работе. Начнем с отправки сообщений!

Отправка сообщений

Одной из особенностей, которую предполагается встроить в при- ложение Spitter, является возможность извещения (возможно, по электронной почте) других пользователей о создании нового со- общения. Эту функцию можно было бы добавить непосредственно в том месте, где производится создание сообщения. Но фактическая

отправка извещений в этом месте может занять продолжительное время и отрицательно сказаться на субъективном восприятии про- изводительности приложения. При добавлении нового сообщения приложение должно откликаться как можно быстрее.

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

Для поддержки асинхронной рассылки извещений введем в при- ложение Spittle службу AlertService:

package com.habuma.spitter.alerts;   import   com.habuma.spitter.domain.Spittle; public  interface  AlertService  {

void  sendSpittleAlert(Spittle  spittle);

}

Как видите, AlertService – это интерфейс, определяющий един- ственный метод sendSpittleAlert(). Класс AlertServiceImpl – это реали- зация интерфейса AlertService, использующая компонент JmsTemplate для отправки объектов Spittle в очередь сообщений с целью их об- работки в более позднее время.

Листинг 13.3. Отправка сообщения с помощью JmsTemplate

package com.habuma.spitter.alerts;

import  javax.jms.JMSException; import   javax.jms.Message; import    javax.jms.Session;

import  org.springframework.beans.factory.annotation.Autowired; import    org.springframework.jms.core.JmsTemplate;

import org.springframework.jms.core.MessageCreator; import   com.habuma.spitter.domain.Spittle;

public class AlertServiceImpl implements AlertService { public  void  sendSpittleAlert(final  Spittle  spittle)  {

jmsTemplate.send(                                                // Отправка  сообщения "spittle.alert.queue",                                     // Имя  приемника

new  MessageCreator()  {

public  Message  createMessage(Session  session) throws JMSException {

return   session.createObjectMessage(spittle);//   Создание

}                                                                                          // сообщения

}

);

}

@Autowired

JmsTemplate  jmsTemplate;

}

Первый параметр метода send() класса JmsTemplate – имя прием- ника JMS, куда отправляется сообщение. При вызове метода send() класс JmsTemplate приобретет JMS-соединение и сеанс и отправит со- общение от имени отправителя (рис. 13.5).

Рис. 13.5. Класс JmsTemplate берет на себя все сложности, связанные с отправкой сообщений

Что касается самого сообщения, оно конструируется с использо- ванием реализации интерфейса MessageCreator, организованной в ви- де анонимного вложенного класса. В методе createMessage() реализа- ции интерфейса MessageCreator программа просто предлагает сеансу создать новый объект сообщения и добавить в него объект Spittle.

Вот и все! Обратите внимание, что метод sendSpittleAlert() за- нимается исключительно сборкой и отправкой сообщения. В нем отсутствует код, управляющий соединением или сеансом, – все это автоматически делает класс JmsTemplate. И в нем отсутствует код, перехватывающий исключения JMSException, – класс JmsTemplate пере- хватывает все исключения JMSException и возбуждает в ответ неконт- ролируемые исключения, перечисленные в табл. 13.1.

Настройка приемника по умолчанию

В листинге 13.3 явно был указан конкретный приемник, куда будут отправляться сообщения методом send(). Такую форму мето- да send() удобно использовать, когда требуется организовать выбор

приемника программно. Но в реализации AlertServiceImpl все сооб- щения отправляются в один и тот же приемник, поэтому преиму- щества данной формы метода send() неочевидны.

Вместо явного определения приемника при отправке каждого со- общения в объявлении компонента JmsTemplate можно было бы опре- делить приемник по умолчанию:

<bean  id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">

<property name="connectionFactory" ref="connectionFactory" />

<property name="defaultDestinationName" value="spittle.alert.queue"/>

</bean>

Теперь вызов метода send()  класса JmsTemplate  можно немного упростить, убрав первый параметр:

jmsTemplate.send(

new  MessageCreator()  {

}

);

Эта форма метода send() принимает только реализацию интерфей- са MessageCreator, а в качестве приемника используется приемник по умолчанию.

Извлечение сообщений

Выше было показано, как с помощью JmsTemplate отправлять со- общения. А как быть на принимающей стороне? Можно ли исполь- зовать класс JmsTemplate для приема сообщений?

Да, можно. Фактически прием сообщений с помощью класса JmsTemplate реализуется еще проще. Чтобы извлечь сообщение, до- статочно вызвать метод receive() класса JmsTemplate, как показано в листинге 13.4.

Листинг 13.4. Прием сообщения с помощью класса JmsTemplate

public  Spittle  getAlert()  { try  {

ObjectMessage  receivedMessage  =

(ObjectMessage) jmsTemplate.receive();       // Прием сообщения

return (Spittle) receivedMessage.getObject();  // Извлечение объекта

} catch (JMSException jmsException)  {

// Возбудить преобразованное исключение

throw   JmsUtils.convertJmsAccessException(jmsException);

}

}

Метод receive() класса JmsTemplate пытается получить сообщение, обратившись к брокеру сообщений. Если сообщение недоступно, ме- тод receive() будет ждать, пока сообщение не станет доступно. Это взаимодействие изображено на рис. 13.6.

Рис. 13.6. Прием сообщений из темы или из очереди

с помощью класса JmsTemplate осуществляется простым вызовом его метода receive(). Обо всем остальном позаботится

сам класс JmsTemplate

Поскольку известно, что в приложении Spitter отправляемые со- общения содержат объект, их можно привести к типу ObjectMessage при приеме. А затем вызвать метод getObject(), чтобы извлечь объект Spittle из сообщения ObjectMessage и вернуть его.

Единственная неприятность, которая здесь поджидает, – необхо- димость предпринять какие-то действия в случае появления исклю- чения JMSException. Как уже упоминалось, класс JmsTemplate прекрасно справляется со всеми контролируемыми исключениями, наследни- ками JMSException, возбуждая в ответ соответствующие им неконтро- лируемые исключения, наследники JmsException. Но это относится только к вызовам методов класса JmsTemplate. Класс JmsTemplate не сможет помочь обработать исключение JMSException, которое может быть возбуждено методом getObject()  класса ObjectMessage.

Поэтому необходимо либо перехватить исключение JMSException, либо объявить, что данный метод может возбуждать его. В соот- ветствии с философией фреймворка Spring, которая призывает из- бегать контролируемых исключений, было решено не позволять ис- ключению JMSException покидать пределы этого метода и перехватить его. В блоке catch можно воспользоваться методом convertJmsAccess- Exception() класса JmsUtils, входящего в состав фреймворка Spring, чтобы преобразовать контролируемое исключение JMSException в не-

контролируемое исключение JmsException. В действительности имен- но так класс JmsTemplate преобразует исключения.

Самым большим недостатком операции извлечения сообщений

с помощью класса JmsTemplate является синхронность метода receive(). Это означает, что приложение вынуждено будет ждать появления сообщения, так как метод receive() окажется заблокированным до момента, пока сообщение не станет доступно (или пока не истечет предельное время ожидания). Не кажется ли вам странной синхрон- ность при приеме сообщения, которое посылается асинхронно?

Решить эту проблему нам помогут простые Java-объекты (POJO), управляемые сообщениями. Посмотрим, как организовать асинхрон- ный прием сообщений с помощью компонентов, реагирующих на сообщения, а не ожидающих их появления.

Источник:   Уоллс К., Spring в действии. – М.: ДМК Пресс, 2013. – 752 с.: ил.

По теме:

  • Комментарии