消息驱动bean主要使用在异步的业务中,比如用户在页面上查询了话费(余额,实时话费,语音,短信。。。),我们需要把用户这个行为记录到数据库中,就可以使用消息驱动bean。
首先使用springAOP 拦截到 调用的 业务方法,发现是业务关注的方法的时候,在invoke 方法中发送一条消息到消息队列中去,(此业务场景我们使用 点对点的 消息发送模式)然后 ,消息驱动bean从监听的队列中取到消息,插入到数据库中。查询话费 和 将行为入库 是异步的,因此不会阻塞。不影响前台页面显示速度。
开发一个消息驱动bean 的步骤:
1.首先 写 消息驱动bean ,负责从消息队列中取消息,然后执行相应的业务方法
package cn.com.xinli.ejb.mdb; import javax.ejb.EJBException; import javax.ejb.MessageDrivenBean; import javax.ejb.MessageDrivenContext; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; import org.apache.log4j.Logger; public class MDBean implements MessageDrivenBean,MessageListener { Logger log=Logger.getLogger(MDBean.class); private transient MessageDrivenContext mdc = null; public void onMessage(Message message) { if(message instanceof TextMessage) { TextMessage msg=(TextMessage) message; try { log.info("接收到消息:"+msg.getText()); } catch (JMSException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { log.info("消息类型不正确!"); } } public void ejbCreate() { } public void ejbRemove() throws EJBException { // TODO Auto-generated method stub } public void setMessageDrivenContext(MessageDrivenContext mdc) throws EJBException { this.mdc = mdc; // TODO Auto-generated method stub } }
2. 配置 ejb-jar.xml
<?xml version="1.0" encoding="gb2312"?> <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd"> <ejb-jar> <description>ejb</description> <display-name>myEJBTest</display-name> <enterprise-beans> <message-driven id="ejb_mdb"> <ejb-name>mdb</ejb-name> <ejb-class>cn.com.xinli.ejb.mdb.MDBean</ejb-class> <transaction-type>Bean</transaction-type> <acknowledge-mode>Auto-acknowledge</acknowledge-mode> <message-driven-destination> <destination-type>javax.jms.Queue</destination-type> </message-driven-destination> </message-driven> </enterprise-beans> </ejb-jar>
3. 配置 jboss.xml
<?xml version="1.0" encoding="gb2312"?> <jboss> <message-driven> <ejb-name>mdb</ejb-name> <configuration-name>Standard Message Driven Bean</configuration-name> <!-- 消息驱动bean 监听的消息队列的 JNDI <destination-jndi-name>queue/mdb</destination-jndi-name> --> </message-driven> </enterprise-beans> </jboss>
4. 讲bean 打成jar包 放在 D:\jboss-4.0.4.GA\server\default\deploy 下
5. 在jboss中配置消息队列 也在 D:\jboss-4.0.4.GA\server\default\deploy 下 ,配置文件必须 符合 *-service.xml 的命名规范,这里我们叫 bsn-service.xml
<?xml version="1.0" encoding="UTF-8"?> <!-- $Id: jbossmq-destinations-service.xml,v 1.4.6.1 2004/11/16 04:32:39 ejort Exp $ --> <!-- | This file defines the default Queues and Topics that JBossMQ | ships with. The default Queues and Topics are used by the | JBoss test suite and by the sample jms programs. | | You can add other destinations to this file, or you can create other | *-service.xml files to contain your application's destinations. --> <server> <!-- Destination without a configured SecurityManager or without a a SecurityConf will default to role guest with read=true, write=true, create=false. --> <mbean code="org.jboss.mq.server.jmx.Queue" name="jboss.mq.destination:service=Queue,name=queueRecordb"> <attribute name="JNDIName">queue/mdb</attribute> <depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends> </mbean> </server>
6.发布
7.客户端测试,观察 JBOSS 控制台 会有
09:43:36,156 INFO [MDBean] 接收到消息:你好
package cn.com.xinli.ejb.test; import java.util.Properties; import javax.jms.Destination; import javax.jms.MessageProducer; import javax.jms.QueueConnection; import javax.jms.QueueConnectionFactory; import javax.jms.QueueSession; import javax.jms.TextMessage; import javax.naming.InitialContext; import org.apache.log4j.Logger; public class Test { static Logger log=Logger.getLogger(Test.class); /** * @param args */ public static void main(String[] args) { Properties props = new Properties(); props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); props.setProperty("java.naming.provider.url", "localhost:1099"); props.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming"); try { InitialContext ctx = new InitialContext(props); QueueConnectionFactory factory=(QueueConnectionFactory)ctx.lookup("QueueConnectionFactory"); QueueConnection conn=factory.createQueueConnection(); QueueSession session=conn.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE); Destination destination=(Destination)ctx.lookup("queue/mdb"); MessageProducer producer = session.createProducer(destination); TextMessage msg = session.createTextMessage("你好"); producer.send(msg); log.info("发送消息成功!!"); /* Object objRef = ctx.lookup("helloEJB"); HelloWorldHome home = (HelloWorldHome) PortableRemoteObject.narrow(objRef, HelloWorldHome.class); HelloWorldRemote remote = home.create(); log.info(remote.sayHello()); */ } catch (Exception ex) { ex.printStackTrace(); } } }
备注:
1. 许多教程上说 必须要到 D:\jboss-4.0.4.GA\server\default\deploy 配置一个 消息队列 类似 bsn-service.xml
其实这不是必要的
如果我们没有配置 a ,b
a. <!-- 消息驱动bean 监听的消息队列的 JNDI-->
<destination-jndi-name>queue/mdb</destination-jndi-name>
b. 也没有配置 bsn-service.xml 那么JBOSS启动的时候发现没有队列和消息驱动bean 关联,则它会自己创建一个消息队列 ,这个消息对列的命名规范 是 : queue/消息驱动bean的jndi
客户端就可以往这个队列 上发送消息。
2. 如果我们配置了 消息驱动bean 关联的队列 和 JBosS下的 bsn-service.xml ,那么我们就可以使用自定义的 队列的JNDI了 比如
a. 配置 mdb 这个消息驱动bean 监听的 对列的JNDI 为
<destination-jndi-name>myqueue</destination-jndi-name>
b.配置 bsn-service.xml的内容为
<?xml version="1.0" encoding="UTF-8"?> <!-- $Id: jbossmq-destinations-service.xml,v 1.4.6.1 2004/11/16 04:32:39 ejort Exp $ --> <!-- | This file defines the default Queues and Topics that JBossMQ | ships with. The default Queues and Topics are used by the | JBoss test suite and by the sample jms programs. | | You can add other destinations to this file, or you can create other | *-service.xml files to contain your application's destinations. --> <server> <!-- Destination without a configured SecurityManager or without a a SecurityConf will default to role guest with read=true, write=true, create=false. --> <mbean code="org.jboss.mq.server.jmx.Queue" name="jboss.mq.destination:service=Queue,name=queueRecordb"> <attribute name="JNDIName">myqueue</attribute> <depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends> </mbean> </server>
那么客户端就可以往 我们自定义的 队列的JDNI上 myqueue 发送消息了
3. 在JBOSS中有两个默认的工厂 一个是 QueueConnectionFactory
另外一个是 TopicConnectionFactory ,这两个工厂是由JBOSS容器帮我们建立的 ,不需要我们去配置,而在websphere 上建立消息驱动bean的时候 就需要我们配置 连接工厂了
4. 以上模拟的的消息驱动bean 是队列模式 ,主题模式何其类似
附件中式 全部的工程代码