项目的后台要求在更改密码后发送邮件通知用户,为了避免发送邮件时程序对用户操作的阻塞,之前中文版中使用了线程来发送邮件,而在英文版中,我决定使用 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对象。
此程序实现了MessageConverter接口,并实现其中的fromMessage和toMessage方法,分别实现转换接收到的消 息为User对象和转换User对象到消息。
我们在程序中使用的是ActiveMQObjectMessage,它是ActiveMQ 中 对javax.jms.ObjectMessage的一个实现。
此时,我们已经完成了JMS Connection Factory和用于发送JMS消息的JMS Template配置,接下来,应该编写发送消息的Bean了,代码如下:
代码很简单,sendUserLoginInformationMail方法是唯一我们需要编写的,调用JMSTemplate的 convertAndSend方法,Spring会自己调用我们之前配置的converter来转换我们发送的User对象。
将此Java在Spring中进行配置,然后在Controller中进行调用即可实现发送User对象到JMS。
到此为止,我们已经实现了消息的发送,现在我们来实现消息的接收。
相对于发送,消息的接收的配置要相对简短些,我们使用MDP(Message Drive POJO)来实现消息的异步接收。我们需要实现javax.jms.MessageListener接口的void onMessage(Message message)方法来接收消息。不过我们可以使用Spring中提供的MessageListenerAdapter来简化接收消息的代码。
我们先写处理消息的接口和实现类:
配置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>
ActiveMQ与Spring
最新推荐文章于 2021-03-06 19:11:39 发布