上一篇已经知道了JMS的基本操作,今天来看一下ejb3中的一种重要bean:Message Drive Bean(mdb)
如果要不断监听一个队列中的消息,通常我们需要写一个监听程序,这需要一定的开发量,而且如果要实现高并发处理,也不易扩展,而MDB则自动实现了该功能,简单点讲,MDB的应用部署到jboss后,能自动监听目标队列,一旦有消息接收,会触发onMessage事件,开发人员可以在该事件处理中扩展自己的业务逻辑.
一、定义一个MDB
1 package mdb; 2 3 4 5 import javax.ejb.ActivationConfigProperty; 6 import javax.ejb.MessageDriven; 7 import javax.jms.JMSException; 8 import javax.jms.Message; 9 import javax.jms.MessageListener; 10 import javax.jms.TextMessage; 11 12 import util.LoggerUtil; 13 14 @MessageDriven(name = "HelloWorldQueueMDB", activationConfig = { 15 @ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"), 16 @ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/queue/mytest"), 17 @ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") }) 18 public class HelloWorldMDB implements MessageListener { 19 20 @Override 21 public void onMessage(Message msg) { 22 TextMessage txtMsg = null; 23 try { 24 if (msg instanceof TextMessage) { 25 txtMsg = (TextMessage) msg; 26 String msgContent = txtMsg.getText(); 27 LoggerUtil.info("Received Message from queue: " + msgContent); 28 } else { 29 LoggerUtil.warning("Message of wrong type: " 30 + txtMsg.getClass().getName()); 31 } 32 } catch (JMSException e) { 33 throw new RuntimeException(e); 34 } 35 36 } 37 38 }
注意该类上的注解,它表明了要监听哪个Queue(可以参考上一篇的内容,先在jboss中建好该queue),其它没什么特别的,把它放一个dynamic web中,打成war包部署到jboss上,为演示效果,部署后,先不启动该应用
附:pom.xml文件的内容
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>cnblogs</groupId> 8 <artifactId>helloworld-mdb</artifactId> 9 <version>0.0.1-SNAPSHOT</version> 10 <packaging>war</packaging> 11 <name>helloworld-mdb</name> 12 13 <dependencyManagement> 14 <dependencies> 15 <dependency> 16 <groupId>org.jboss.bom</groupId> 17 <artifactId>jboss-javaee-6.0-with-tools</artifactId> 18 <version>1.0.7.Final</version> 19 <type>pom</type> 20 <scope>import</scope> 21 </dependency> 22 </dependencies> 23 </dependencyManagement> 24 25 <dependencies> 26 <dependency> 27 <groupId>org.jboss.spec.javax.jms</groupId> 28 <artifactId>jboss-jms-api_1.1_spec</artifactId> 29 <scope>provided</scope> 30 </dependency> 31 <dependency> 32 <groupId>org.jboss.spec.javax.ejb</groupId> 33 <artifactId>jboss-ejb-api_3.1_spec</artifactId> 34 <scope>provided</scope> 35 </dependency> 36 </dependencies> 37 38 </project>
二、测试验证
a) 可以参考上一篇JMS的内容,另建一个常规的project,向该队列发送消息(注意:仅发送,不要接收,否则消息被收走了,MDB就收不到消息了)
1 package jms; 2 3 4 import java.util.Hashtable; 5 6 import javax.jms.Connection; 7 import javax.jms.ConnectionFactory; 8 import javax.jms.Destination; 9 import javax.jms.JMSException; 10 import javax.jms.MessageConsumer; 11 import javax.jms.MessageProducer; 12 import javax.jms.Session; 13 import javax.jms.TextMessage; 14 import javax.naming.Context; 15 import javax.naming.InitialContext; 16 import javax.naming.NamingException; 17 18 public class App { 19 20 public static void main(String[] args) throws NamingException, JMSException { 21 22 final String lOOKUP_CONNECTION_FACTORY_NAME = "lookup.connectionfactory.name"; 23 final String lOOKUP_DESTINATION_NAME = "lookup.destination.name"; 24 25 ConnectionFactory connectionFactory = null; 26 Connection connection = null; 27 Session session = null; 28 MessageProducer producer = null; 29 MessageConsumer consumer = null; 30 Destination destination = null; 31 TextMessage message = null; 32 Context context = null; 33 34 try { 35 // 创建上下文(默认会从应用的classpath下加载jndi.properties做为环境参数) 36 context = new InitialContext(); 37 38 // 把环境参数取出来,后面会用到 39 Hashtable<String, String> env = (Hashtable<String, String>) context 40 .getEnvironment(); 41 42 // 查找连接工厂 43 connectionFactory = (ConnectionFactory) context.lookup(env 44 .get(lOOKUP_CONNECTION_FACTORY_NAME)); 45 46 // 查找目标队列 47 destination = (Destination) context.lookup(env 48 .get(lOOKUP_DESTINATION_NAME)); 49 50 // 创建连接 51 connection = connectionFactory.createConnection( 52 env.get(Context.SECURITY_PRINCIPAL), 53 env.get(Context.SECURITY_CREDENTIALS)); 54 55 // 创建会话 56 session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); 57 58 // 创建生产者(即发送者) 59 producer = session.createProducer(destination); 60 61 // 创建消费者(即接收者) 62 consumer = session.createConsumer(destination); 63 64 // 开始连接 65 connection.start(); 66 67 // 发送消息 68 69 message = session.createTextMessage("HELLO,I AM GLAD TO SEE YOU!"); 70 71 producer.send(message); 72 73 System.out.println("发送成功!"); 74 75 76 77 } catch (NamingException e) { 78 e.printStackTrace(); 79 } catch (JMSException e) { 80 e.printStackTrace(); 81 } finally { 82 // 释放资源 83 if (context != null) { 84 context.close(); 85 } 86 87 if (connection != null) { 88 connection.close(); 89 } 90 91 } 92 } 93 94 }
b) 然后在jboss中,再把该应用启用起来,观察console窗口的输出:
三、xml方式配置MDB
刚才我们是用注解方式来配置MDB的,这种方式不需要xml配置文件,十分方便,但是也有缺点,配置与代码紧耦合,如果以后要修改queue名称,就得改代码,重新编译,所以jboss也提供了xml配置方式
方法:在META-INF(非web项目)或WEB-INF(web项目)放置一个名为jboss-ejb3.xml(这是固定名称,不要修改!)
内容参考下面这样:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee" 3 xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:c="urn:clustering:1.0" 5 xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd" 6 version="3.1" impl-version="2.0"> 7 <enterprise-beans> 8 <message-driven> 9 <ejb-name>HelloWorldQueueMDB</ejb-name> 10 <ejb-class>mdb.HelloWorldMDB</ejb-class> 11 <activation-config> 12 <activation-config-property> 13 <activation-config-property-name>destinationType</activation-config-property-name> 14 <activation-config-property-value>javax.jms.Queue</activation-config-property-value> 15 </activation-config-property> 16 <activation-config-property> 17 <activation-config-property-name>destination</activation-config-property-name> 18 <activation-config-property-value>jms/queue/mytest</activation-config-property-value> 19 </activation-config-property> 20 <activation-config-property> 21 <activation-config-property-name>acknowledgeMode</activation-config-property-name> 22 <activation-config-property-value>Auto-acknowledge</activation-config-property-value> 23 </activation-config-property> 24 </activation-config> 25 </message-driven> 26 </enterprise-beans> 27 <assembly-descriptor> 28 <c:clustering> 29 <ejb-name>DDBasedClusteredSFSB</ejb-name> 30 <c:clustered>true</c:clustered> 31 </c:clustering> 32 </assembly-descriptor> 33 </jboss:ejb-jar>
然后把HelloWorldQueueMDB类上的那一堆注解全注释掉,再跑下,顺利的话,也同样可以接收消息
示例源代码下载:mdb-sample.zip