消息驱动Bean(MDB)的扩展JBOSS引入了两个注释@Cosumer和@Producer来扩展MDB,可以让使用MDB“看起来”和使用会话Bean一样简单。为了解释这两个注释,我们先来看看这两个注释的定义。
package org.jboss.annotation.ejb; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME)public @interface Consumer{ String name() default ""; TransactionManagementType transactionManagement() default TransactionManagementType.CONTAINER; ActivationConfigProperty[] activationConfig() default {};} package org.jboss.annotation.Producer; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME)public @interface Producer{ String connectionFactory() default ""; boolean transacted() default false; int acknowledgeMode() default 1; // autoacknowledge} 可以看出@Consumer跟EJB3.0规范的@MessageDriven注释,除了名称不同外,其实都一样。@Producer注释定义了JMS连接工厂,事务和消息回复类型(默认是自动回复)
全部的“魔法”都在剩下的这个@Producer上。JBOSS EJB3.0规范规定所有用@Cosumer注释的MDB必须实行用@Producer注释的接口。下面是一个简单的例子:
import org.jboss.ejb3.mdb.Consumer; @Consumer(activationConfig = { @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"), @ActivationConfigProperty(propertyName="destination", propertyValue="queue/mdbtest") })public class QueueTestConsumer implements QueueTestRemote{ public void method1(String msg, int num) { System.out.println("method1(" + msg + ", " + num + ")"); }} 接口QueueTestRemote代码如下:@Producerpublic interface QueueTestRemote { public void method1(String msg, int num);}这里就出现个奇怪的事情,按照我们的原来对MDB的理解是:消息的生产者(Producer)产生消息,通过JMS API把消息发送给MDB来消费(Consumer)。因此MDB是消费者,应该实现一个“消费者”的接口,而不是“生产者”的接口。
为了解释这个原因,我们再来看看,消息生产者如何调用MDB的代码。
import org.jboss.ejb3.mdb.ProducerObject;import org.jboss.ejb3.mdb.ProducerManager; public static void main(String[] args) throws Exception{ InitialContext ctx = new InitialContext(); QueueTestRemote tester = (QueueTestRemote)ctx.lookup(QueueTestRemote.class.getName()); ProducerObject po = (ProducerObject)tester; ProducerManager manager = po.getProducerManager(); manager.connect(); // internally create a JMS connection try { tester.method1("hello world", 55); } finally { manager.close(); // clean up the JMS connection }}
我们来研究一下上面的代码。
QueueTestRemote tester = (QueueTestRemote)ctx.lookup(QueueTestRemote.class.getName());用MDB实现的接口QueueTestRemot(就是用@Producer注释的那个接口)的全限定名从JNDI中查找出存根,
接着,把存根类型转换成“ProducerObject”,ProducerObject是JBOSS扩展的一个接口。在部署MDB时,JBOSS会为每个被@Producer注释的接口自动生成一个Proxy(或者叫存根),并注册到JNDI中去。这个Proxy不仅实现@Procdure接口里的方法,还实现了这个ProducerObject方法。ProducerObject里只有一个方法:ProducerManager getProducerManager(); 再来看看ProducerManager,它也是一个接口,被ProducerManagerImpl实现。里面有如下方法
void close()
void commit()
void connect()
void rollback()
void setPassword(String passwd)
void setUsername(String user)
这些方法都是用来跟JMS服务器连接的一些方法。
再来看一下这句:tester.method1("hello world", 55);
执行这句时,参数“hello world”和55被打包成JMS消息,传给MDB的method1方法。
实现了调用MDB的method1的目的。
从上面的论述可以看出,JBOSS对整个JMS消息传递过程进行了“屏蔽”,使得我们不再需要对要传递的参数进行打包,在MDB端也不用拆包。这个功能在一定程度上减少了开发量。
我们再来看看其他注释的功能:
1,@ProducerLocal。上面我们提到了@Producer注释,里面有一个ConnectionFactory属性,它的默认值是“ConnectionFacotory”,如果你使用这个@ProducerLocal注释,默认值将是“java:/ConnectionFactroy”,即在本地(JVM)的JNDI中查找JMS连接工厂
2,@MessageProperties。这个注释用来设置JMS消息的属性。默认的属性是
timeToLive=0,Priority=4,delivery=DeliveryMode.PERSISTENT
各个属性的意义:
属性名称
| 意义
| TimeToLive
| 消息过期时间,被设为零,表示该消息永不过期。如果发送后,在消息过期时间之后消息还没有被发送到目的地,则该消息被清除。
| Prioprity
| 消息优先级,从0-9 十个级别,0-4 是普通消息,5-9 是加急消息。
| Delivery
| 传递模式, 有两种模式: PERSISTENT 和NON_PERSISTENT,PERSISTENT 表示该消息一定要被送到目的地,否则会导致应用错误。NON_PERSISTENT 表示偶然丢失该消息是被允许的,
|
我们也可以自定义这些属性,
@Producerpublic interface QueueTestRemote { public void method1(String msg, int num); @MessageProperties(delivery=DeliveryMode.NON_PERSISTENT, timeToLive=1000, priority=1) public void method2(String msg, int num);}
得到当前的消息
有时候,我们需要直接得到“原始的”JMS消息,比如说得到消息的回复地址,回复消息或者其他什么原因。我们就可以使用@org.jboss.annotation.ejb.CurrentMessage这个注释。
例子:
@CurrentMessage private javax.jms.Message currentMessage;在MDB的消息监听方法被调用前,JBOSS 会把JMS消息注入到currentMessage这个私有变量。 |