zoukankan      html  css  js  c++  java
  • Spring中使用ActiveMQ发送邮件

    项目的后台要求在更改密码后发送邮件通知用户,为了避免发送邮件时程序对用户操作的阻塞,之前中文版中使用了线程来发送邮件,而在英文版中,我决定使用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; 

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

    import org.apache.activemq.command.ActiveMQObjectMessage; 
    import org.apache.commons.logging.Log; 
    import org.apache.commons.logging.LogFactory; 
    import org.springframework.jms.support.converter.MessageConverter; 

    import com.tiandinet.jms.sample.User; 

    /** 
    * Converte User message. 

    * @author Yangtze 
    */ 
    public class UserMessageConverter implements MessageConverter { 

        private static transient Log logger = LogFactory.getLog(UserMessageConverter.class); 

        /** 
         * {@inheritDoc} 
         * 
         * @see org.springframework.jms.support.converter.MessageConverter 
         * #fromMessage(javax.jms.Message) 
         */ 
        public Object fromMessage(Message message) throws JMSException { 
            if (logger.isDebugEnabled()) { 
                logger.debug("Receive JMS message: " + message); 
            } 

            if (message instanceof ObjectMessage) { 
                ObjectMessage oMsg = (ObjectMessage) message; 

                if (oMsg instanceof ActiveMQObjectMessage) { 
                    ActiveMQObjectMessage aMsg = (ActiveMQObjectMessage) oMsg; 

                    try { 
                        User user = (User) aMsg.getObject(); 

                        return user; 
                    } catch (Exception e) { 
                        logger.error("Message:[" + message + "] is not a instance of User."); 
                        throw new JMSException("Message:[" + message + "] is not a instance of User."); 
                    } 
                } else { 
                    logger.error("Message:[" + message + "] is not " 
        + "a instance of ActiveMQObjectMessage[User]."); 
                    throw new JMSException("Message:[" + message + "] is not " 
        + "a instance of ActiveMQObjectMessage[User]."); 
                } 
            } else { 
                logger.error("Message:[" + message + "] is not a instance of ObjectMessage."); 
                throw new JMSException("Message:[" + message + "] is not a instance of ObjectMessage."); 
            } 
        } 

        /** 
         * {@inheritDoc} 
         * 
         * @see org.springframework.jms.support.converter.MessageConverter#toMessage(java.lang.Object, 
         *      javax.jms.Session) 
         */ 
        public Message toMessage(Object obj, Session session) throws JMSException { 
            if (logger.isDebugEnabled()) { 
                logger.debug("Convert User object to JMS message: " + obj); 
            } 

            if (obj instanceof User) { 
                ActiveMQObjectMessage msg = (ActiveMQObjectMessage) session.createObjectMessage(); 
                msg.setObject((User) obj); 

                return msg; 
            } else { 
                logger.error("Object:[" + obj + "] is not a instance of User."); 
                throw new JMSException("Object:[" + obj + "] is not a instance of User."); 
            } 
        } 

      此程序实现了MessageConverter接口,并实现其中的fromMessage和toMessage方法,分别实现转换接收到的消息为User对象和转换User对象到消息。 
    我们在程序中使用的是ActiveMQObjectMessage,它是ActiveMQ中对javax.jms.ObjectMessage的一个实现。 
      此时,我们已经完成了JMS Connection Factory和用于发送JMS消息的JMS Template配置,接下来,应该编写发送消息的Bean了,代码如下: 

    package com.tiandinet.jms.sample; 

    import org.springframework.jms.core.JmsTemplate; 

    import com.tiandinet.jms.sample.User; 

    /** 
    * Send user's login information mail via JMS. 

    * @author Yangtze 
    */ 
    public class UserMessageProducerImpl implements IUserMessageProducer { 

        private JmsTemplate jmsTemplate; 

        /** 
         * {@inheritDoc} 
         * 
         * @see com.tiandinet.jms.sample.IUserMessageProducer 
         * #sendUserLoginInformationMail(com.tiandinet.jms.sample.User) 
         */ 
        public void sendUserLoginInformationMail(User user) { 
            getJmsTemplate().convertAndSend(user); 
        } 

        /** 
         * Return the jmsTemplate. 
         * 
         * @return the jmsTemplate 
         */ 
        public final JmsTemplate getJmsTemplate() { 
            return jmsTemplate; 
        } 

        /** 
         * Set the jmsTemplate. 
         * 
         * @param jmsTemplate 
         *            the jmsTemplate to set 
         */ 
        public final void setJmsTemplate(JmsTemplate jmsTemplate) { 
            this.jmsTemplate = jmsTemplate; 
        } 


      代码很简单,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; 

    import javax.jms.JMSException; 
    import javax.jms.ObjectMessage; 

    import com.tiandinet.jms.sample.User; 

    /** 
    * JMS message handler. 

    * Yangtze 
    */ 
    public interface IMessageConsumer { 

        /** 
         * Handle the user message. 
         * 
         * @param user 
         *            User 
         * @throws JMSException 
         *             exception 
         */ 
        void handleMessage(User user) throws JMSException; 


    package com.tiandinet.jms.sample; 

    import javax.jms.JMSException; 

    import org.apache.commons.logging.Log; 
    import org.apache.commons.logging.LogFactory; 

    import com.tiandinet.jms.sample.User; 
    import com.tiandinet.jms.sample.IMailService; 

    /** 
    * JMS message handler - for User Message. 

    * Yangtze 
    */ 
    public class UserMessageConsumerImpl implements IMessageConsumer { 

        private static transient Log logger = LogFactory.getLog(UserMessageConsumerImpl.class); 

        private IMailService mailService; 

        /** 
         * {@inheritDoc} 
         * 
         * @see com.tiandinet.jms.sample.IMessageConsumer 
    * #handleMessage(com.tiandinet.jms.sample.User) 
         */ 
        public void handleMessage(User user) throws JMSException { 
            if (logger.isDebugEnabled()) { 
                logger.debug("Receive a User object from ActiveMQ: " + user.toString()); 
            } 

            mailService.sendUserLoginInforMail(user); 
        } 


      配置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> 
  • 相关阅读:
    JQuery制作网页——第五章 初识 jQuery
    JQuery制作网页—— 第四章JavaScript对象及初识面向对象
    JQuery制作网页—— 第三章 JavaScript操作DOM对象
    JQuery制作网页—— 第二章 JavaScript操作BOM对象
    JQuery制作网页—— 第一章 JavaScript基础
    JAVA / MySql 编程——第八章 DAO 模式
    JAVA / MySql 编程——第七章 JDBC
    JAVA / MySql 编程——第六章 Mysql 创建账户的相关命令
    JAVA / MySql 编程——第五章 事务、视图、索引、备份和恢复
    JAVA / MySql 编程—— 第四章 高级查询(二)
  • 原文地址:https://www.cnblogs.com/chenying99/p/2744086.html
Copyright © 2011-2022 走看看