ActiveMQ與Spring

來源:互聯網
上載者:User

項目的後台要求在更改密碼後發送郵件通知使用者,為了避免發送郵件時程式對使用者操作的阻塞,之前中文版中使用了線程來發送郵件,而在英文版中,我決定使用
JMS來非同步發送郵件,讓使用者更改密碼的操作和發送郵件的操作更進一步解耦,也在實際環境中試試JMS。

  我們的環境是Spring 2.5, Tomcat 5.5,使用ActiveMQ

實現JMS傳送和接收。

  首先,我們在Spring中加入ActiveMQ
Broker的配置:

     <bean id="broker"

        class="org.apache.activemq
.xbean.BrokerFactoryBean">

        <property name="config"

            value="classpath:activemq
.xml"
/>

        <property name="start"

            value="true" />

    </bean>

  我們在此處配置了BrokerFactoryBean,此Bean實現在Spring中配置嵌入式Broker,並且支援XBean方式的配
置。Broker的設定檔由config屬性指定,此處定義設定檔位於classpath中的activemq
.xml。

  接下來我們需要建立Broker的設定檔activemq
.xml。其實我們
不需要從頭配置,展開ActiveMQ
的jar包,在org.apache.activemq
.xbean下,就有一個activemq
.xml,
我們將其拷貝到WEB-INF/classes/目錄下,並進行修改。

下面是activemq
.xml的內容:

<beans>

    <bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
/>

    <broker useJmx="false"

        persistent="false"

        xmlns="http://activemq
.apache.org/schema/core">

        <transportconnectors>

            <transportconnector uri="tcp://localhost:61636" />

        </transportconnectors>

        <networkconnectors></networkconnectors>

    </broker>

</beans>

  在broker中,我們指定了不開啟JMX,並且不使用持久化(persistent=”false”)。

  如果不對訊息進行持久化儲存,在容器或者JVM關閉、重啟,或者崩潰後,所有的訊息都將丟失,在我們的業務中,對於發送密碼更改通知訊息,並
非是重要的功能,所以我們選擇不使用持久化儲存,但對於不同商務邏輯,可能會需要進行持久化儲存。ActiveMQ

供的持久化儲存方案可以將訊息儲存到檔案系統、資料庫等。

  要在Broker中開啟持久化儲存,需要設定persistent為true,並且對其子節點persistenceAdapter,
journaledJDBC進行配置。ActiveMQ
jar包中的activemq
.xml有被注釋掉的樣本,可以參考。

  接著我們在Spring中配置JMS Connection Factory。

     <bean id="jmsFactory"

        class="org.apache.activemq
.ActiveMQConnectionFactory">

        <property name="brokerURL"

            value="tcp://localhost:61636" />

    </bean>

  注意其中的borkerURL,應該是你在activemq
.xml中
transportconnector節點的uri屬性,這表示JMS Server的監聽地址。

  配置訊息發送目的地:

     <bean id="topicDestination"

        class="org.apache.activemq
.command.ActiveMQTopic">

        <constructor -arg value="MY.topic" />

    </bean>

    <bean id="queueDestination"

        class="org.apache.activemq
.command.ActiveMQQueue">

        <constructor -arg value="MY.queue" />

    </bean>

  在JMS中,目的地有兩種:主題(topic)和隊列(queue)。兩者的區別是:當一個主題目的地中被放入了一個訊息後,所有的訂閱者都
會收到通知;而對於隊列,僅有一個“訂閱者”會收到這個訊息,隊列中的訊息一旦被處理,就不會存在於隊列中。顯然,對於郵件發送程式來說,使用隊列才是正
確的選擇,而使用主題時,可能會發送多封相同的郵件。

  Topic和Queue只是JMS內部以及其處理方式的不同,對於訊息發送方和接收方來說,完全沒有代碼上的區別。

  配置Spring中訊息發送的JMS Template:

     <bean id="producerJmsTemplate"

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

        <property name="connectionFactory">

            <bean
class="org.springframework.jms.connection.SingleConnectionFactory">

                <property name="targetConnectionFactory"

                    ref="jmsFactory" />

            </bean>

        </property>

        <property name="defaultDestination"

            ref="queueDestination" />

        <property name="messageConverter"

            ref="userMessageConverter" />

    </bean>

  注意此處的defaultDestination使用的是基於Queue的目的地。

  在實際的訊息發送中,郵件內容需要用到User.username, User.password, User.email,
User.fullname,顯示如果我們直接發送User對象到訊息佇列,接收的時候也能直接取出User對象,那麼在郵件發送程式中操作就會方便許
多,所以在些處,我們定義了messageConverter屬性,他指定了發送訊息時使用的訊息轉換bean,這樣,在直接發送User到JMS隊列
時,Spring會自動幫我們進行轉換,下面是Converter的配置和代碼:

     <bean id="userMessageConverter"

        class="com.tiandinet.jms.sample.UserMessageConverter" />

  此轉換器同樣也會使用在訊息接收中,將接收到的訊息轉換為User對象。

package com.tiandinet.jms.sample;<br />import javax.jms.JMSException;<br />import javax.jms.Message;<br />import javax.jms.ObjectMessage;<br />import javax.jms.Session;<br />import org.apache.activemq.command.ActiveMQObjectMessage;<br />import org.apache.commons.logging.Log;<br />import org.apache.commons.logging.LogFactory;<br />import org.springframework.jms.support.converter.MessageConverter;<br />import com.tiandinet.jms.sample.User;<br />/**<br />* Converte User message.<br />*<br />* @author Yangtze<br />*/<br />public class UserMessageConverter implements MessageConverter {<br /> private static transient Log logger = LogFactory.getLog(UserMessageConverter.class);<br /> /**<br /> * {@inheritDoc}<br /> *<br /> * @see org.springframework.jms.support.converter.MessageConverter<br /> * #fromMessage(javax.jms.Message)<br /> */<br /> public Object fromMessage(Message message) throws JMSException {<br /> if (logger.isDebugEnabled()) {<br /> logger.debug("Receive JMS message: " + message);<br /> }<br /> if (message instanceof ObjectMessage) {<br /> ObjectMessage oMsg = (ObjectMessage) message;<br /> if (oMsg instanceof ActiveMQObjectMessage) {<br /> ActiveMQObjectMessage aMsg = (ActiveMQObjectMessage) oMsg;<br /> try {<br /> User user = (User) aMsg.getObject();<br /> return user;<br /> } catch (Exception e) {<br /> logger.error("Message:[" + message + "] is not a instance of User.");<br /> throw new JMSException("Message:[" + message + "] is not a instance of User.");<br /> }<br /> } else {<br /> logger.error("Message:[" + message + "] is not "<br /> + "a instance of ActiveMQObjectMessage[User].");<br /> throw new JMSException("Message:[" + message + "] is not "<br /> + "a instance of ActiveMQObjectMessage[User].");<br /> }<br /> } else {<br /> logger.error("Message:[" + message + "] is not a instance of ObjectMessage.");<br /> throw new JMSException("Message:[" + message + "] is not a instance of ObjectMessage.");<br /> }<br /> }<br /> /**<br /> * {@inheritDoc}<br /> *<br /> * @see org.springframework.jms.support.converter.MessageConverter#toMessage(java.lang.Object,<br /> * javax.jms.Session)<br /> */<br /> public Message toMessage(Object obj, Session session) throws JMSException {<br /> if (logger.isDebugEnabled()) {<br /> logger.debug("Convert User object to JMS message: " + obj);<br /> }<br /> if (obj instanceof User) {<br /> ActiveMQObjectMessage msg = (ActiveMQObjectMessage) session.createObjectMessage();<br /> msg.setObject((User) obj);<br /> return msg;<br /> } else {<br /> logger.error("Object:[" + obj + "] is not a instance of User.");<br /> throw new JMSException("Object:[" + obj + "] is not a instance of User.");<br /> }<br /> }<br />}

  此程式實現了MessageConverter介面,並實現其中的fromMessage和toMessage方法,分別實現轉換接收到的消
息為User對象和轉換User對象到訊息。

我們在程式中使用的是ActiveMQObjectMessage,它是ActiveMQ

對javax.jms.ObjectMessage的一個實現。

  此時,我們已經完成了JMS Connection Factory和用於發送JMS訊息的JMS
Template配置,接下來,應該編寫發送訊息的Bean了,代碼如下:

package com.tiandinet.jms.sample;<br />import org.springframework.jms.core.JmsTemplate;<br />import com.tiandinet.jms.sample.User;<br />/**<br />* Send user's login information mail via JMS.<br />*<br />* @author Yangtze<br />*/<br />public class UserMessageProducerImpl implements IUserMessageProducer {<br /> private JmsTemplate jmsTemplate;<br /> /**<br /> * {@inheritDoc}<br /> *<br /> * @see com.tiandinet.jms.sample.IUserMessageProducer<br /> * #sendUserLoginInformationMail(com.tiandinet.jms.sample.User)<br /> */<br /> public void sendUserLoginInformationMail(User user) {<br /> getJmsTemplate().convertAndSend(user);<br /> }<br /> /**<br /> * Return the jmsTemplate.<br /> *<br /> * @return the jmsTemplate<br /> */<br /> public final JmsTemplate getJmsTemplate() {<br /> return jmsTemplate;<br /> }<br /> /**<br /> * Set the jmsTemplate.<br /> *<br /> * @param jmsTemplate<br /> * the jmsTemplate to set<br /> */<br /> public final void setJmsTemplate(JmsTemplate jmsTemplate) {<br /> this.jmsTemplate = jmsTemplate;<br /> }<br />}
  代碼很簡單,sendUserLoginInformationMail方法是唯一我們需要編寫的,調用JMSTemplate的
convertAndSend方法,Spring會自己調用我們之前配置的converter來轉換我們發送的User對象。

  將此Java在Spring中進行配置,然後在Controller中進行調用即可實現發送User對象到JMS。

  到此為止,我們已經實現了訊息的發送,現在我們來實現訊息的接收。

  相對於發送,訊息的接收的配置要相對簡短些,我們使用MDP(Message Drive
POJO)來實現訊息的非同步接收。我們需要實現javax.jms.MessageListener介面的void onMessage(Message
message)方法來接收訊息。不過我們可以使用Spring中提供的MessageListenerAdapter來簡化接收訊息的代碼。

  我們先寫處理訊息的介面和實作類別:

package com.tiandinet.jms.sample;<br />import javax.jms.JMSException;<br />import javax.jms.ObjectMessage;<br />import com.tiandinet.jms.sample.User;<br />/**<br />* JMS message handler.<br />*<br />* Yangtze<br />*/<br />public interface IMessageConsumer {<br /> /**<br /> * Handle the user message.<br /> *<br /> * @param user<br /> * User<br /> * @throws JMSException<br /> * exception<br /> */<br /> void handleMessage(User user) throws JMSException;<br />}<br />package com.tiandinet.jms.sample;<br />import javax.jms.JMSException;<br />import org.apache.commons.logging.Log;<br />import org.apache.commons.logging.LogFactory;<br />import com.tiandinet.jms.sample.User;<br />import com.tiandinet.jms.sample.IMailService;<br />/**<br />* JMS message handler - for User Message.<br />*<br />* Yangtze<br />*/<br />public class UserMessageConsumerImpl implements IMessageConsumer {<br /> private static transient Log logger = LogFactory.getLog(UserMessageConsumerImpl.class);<br /> private IMailService mailService;<br /> /**<br /> * {@inheritDoc}<br /> *<br /> * @see com.tiandinet.jms.sample.IMessageConsumer<br />* #handleMessage(com.tiandinet.jms.sample.User)<br /> */<br /> public void handleMessage(User user) throws JMSException {<br /> if (logger.isDebugEnabled()) {<br /> logger.debug("Receive a User object from ActiveMQ: " + user.toString());<br /> }<br /> mailService.sendUserLoginInforMail(user);<br /> }<br />} 

  配置message listener

     <bean id="messageListener"

       
class="org.springframework.jms.listener.adapter.MessageListenerAdapter">

        <constructor -arg>

            <bean
class="com.tiandinet.jms.sample.UserMessageConsumerImpl">

                <property name="mailService"

                    ref="mailService" />

            </bean>

        </constructor>

        <property name="defaultListenerMethod"

            value="handleMessage" />

        <property name="messageConverter"

            ref="userMessageConverter" />

    </bean>

  其中的mailService即是我們的郵件發送類,其sendUserLoginInforMail方法實現了郵件發送的功能。

訊息偵聽適配器defaultListenerMethod屬性指定Spring在收到訊息後調用的方法,此處為
handleMessage,Spring會根據收到的訊息User對象,調用handleMessage(User user)方法。

  配置訊息偵聽容器,並指定我們定義的訊息接聽程式。

     <bean id="listenerContainer"

       
class="org.springframework.jms.listener.DefaultMessageListenerContainer">

        <property name="concurrentConsumers"

            value="5" />

        <property name="connectionFactory"

            ref="jmsFactory" />

        <property name="destination"

            ref="queueDestination" />

        <property name="messageListener"

            ref="messageListener" />

    </bean>

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.