在从Session中创建了MessageConsumer之后,如果想同步接收为Destination生成的下一条消息,那么可以调用MessageConsumer上的检索方法。该方法没有采用任何参数,在接收下一个Message之前,它将一直受阻,并且会将这个Message返回给调用者。为了接收异步消息,可以向MessageConsumer注册一个MessageListener接口的实现。这种方法对Topic 和 Queue都适用。MessageListener只有一个您必须实现的方法 —— onMessage,它只接收一个参数,即Message。在为每个发送到Destination的消息实现onMessage时,将调用该方法。Sender.java和Receiver.java中的onMessage实现中示范了这个过程。在Receiver.java中,我们将下面的代码放入initializeJMS方法中,这段代码将创建MessageConsumer(一个QueueReceiver)并设置MessageListener的实现:
  // Create a Receiver for the Queue...
  receiver = session.createReceiver(queue);
  // Set the listener (this class)
  receiver.setMessageListener(this);
  一旦调用了Connection的start方法,消息会在到达Destination之时开始进入Consumer。
  ReplyTo——使用临时队列
  您还会注意到,Sender.java和Receiver.java都实现了MessageConsumers和MessageProducers。二者都实现了MessageListener。这说明了JMS的一个有趣特征,也是说,它使用了临时Destination。希望接收它所产生信息的响应的应用程序可以创建一个临时的Queue或者Topic,并在它发送的Message中传递这个Destination。
  Message的属性之一是JMSReplyTo属性。这个属性是用于这个目的的。您可以创建一个临时的Queue或者Topic,并把它放入Message的JMSReplyTo属性中。收到该消息的消费者有一个私有的临时Destination,可以用它来响应发送者。可以通过两种方法对这一点进行说明,这两种方法分别在两个文件中。Sender.java中包含如下所示代码段,它将创建临时的Queue,并将它放置在TextMessage的JMSReplyTo属性中:
  // Create a temporary queue for replies...
  tempQueue = (Queue) session.createTemporaryQueue();
  以上这行代码可以在Sender.java的initializeJMS方法中找到。这段代码将在启动应用程序时创建一个临时Queue,而这个Queue将存在于应用程序的整个生命周期中。下面这行代码可以在Sender.java的sendMsg方法中找到,它展示了如何设置JMSReplyTo属性,以包含临时的Queue。
  // Set ReplyTo to temporary queue...
  msg.setJMSReplyTo(tempQueue);
  在Receiver.java的QueueReceiver接收这条消息时,会从JMSReplyTo字段中提取临时Queue,并且会通过应用程序构造一个QueueSender,以便将响应消息发送回Sender.java。这展示了如何使用JMS Message的属性,并显示了私有的临时Destination的有用之处。它还展示了客户机可以如何既是消息的Producer,有是消息的Consumer。下面的代码来自Receiver.java,它展示了如何从JMS Message中提取临时Queue;可以在onMessage方法中找到这些代码:
  // Get the temporary queue from the JMSReplyTo
  // property of the message...
  tempQueue = (Queue) msg.getJMSReplyTo();
  以下代码块来自sendReplyToMsg方法,它展示了如何创建QueueSender和如何发送应答:
  // create a Sender for the temporary queue
  if (sender == null)
  sender = session.createSender(tempQueue);
  TextMessage msg = session.createTextMessage();
  msg.setText(REPLYTO_TEXT);
  ...
  // Send the message to the temporary queue...
  sender.send(msg);
  主题
  Topic是为了实现“发布/订阅”消息传递机制而设计的。而Queue是为了拥有一个Producer和一个Consumer而设计的,Topic的设计目标是允许拥有多个能发送消息给它的Producer,同时拥有多个能接收来自Topic的同一消息的Consumer。
  Topic的MessageProducers和MessageConsumers的创建过程与Queue的类似。您可以使用Session来创建TopicPublishers和TopicSubscribers。之所以存在QueueSender和QueueReceiver镜像,是因为它们都提供了特定于Topic的功能,并且都实现了基本的MessageProducer和MessageConsumer接口。
  TopicPublisher的创建过程几乎与QueueSender的创建过程完全相同。以下代码来自Publisher.java的sendMsg方法,它展示了TopicPublisher的创建过程,以及如何向Topic发布消息:
  // create a Publisher if there isnt one...
  if (publisher == null)
  publisher = session.createPublisher(topic);
  TextMessage msg = session.createTextMessage();
  msg.setText(text);
  ...
  // Publish it to the topic...
  publisher.publish(msg);
  持久订阅者
  TopicSubscribers一个有趣的方面是它的持久订阅者,即用户提供的惟一名称,可以在Session中识别这个名称。Session拥有一个与之相关的Client ID,这个ID要么是通过Connection上的一个方法调用在运行时定义的,要么是作为托管ConnectionFactory的一部分(在我们的例子中,它是在weblogic.properties文件中用这种方法定义的)。因此,一个Connection可以提供一个带有Client ID的Session,并且持久订阅者的名称是Session中的惟一标识符,它与特定的Client ID相关联。持久订阅者的目的是为给定的Topic创建一个惟一的、持久的Consumer。
  通过调用TopicSession的createDurableSubscriber方法创建TopicSubscriber的应用程序必须传递一个持久订阅者的名称(一个字符串)作为其参数之一;例如,您可以将持久订阅者的名称设置为目前已登录的用户的名称,等等。这个名称惟一地标识出了某一Topic的特殊订阅者(连同Connection/Session的惟一Client ID)。一旦已经向Topic注册这个持久订阅者,该Topic会持久性地确保消息被发送到这个订阅者那里。这意味着如果某一特殊的持久订阅者是不可用的,那么将会一直保存消息,直到下次这个持久订阅者(有相同的、惟一的持久订阅者名称和Client ID)注册为一个Consumer为止。Subscriber.java 文件展示了持久订阅者的创建过程,并允许您使用默认的订阅者名称,或者从命令行设置这个标识符(要得到关于运行这个应用程序和展示通过拥有持久订阅者获得消息性的更多细节,请参阅“readme.txt”)。下列代码片段来自Subscriber.java的initializeJMS方法,它展示了如何从TopicSession中创建一个持久订阅者:
  subscriber = session.createDurableSubscriber(
  topic,
  subscriberID,
  SELECTOR,
  false);
  // Set the listener (this class)
  subscriber.setMessageListener(this);
  TopicSubscribers并没有被创建为持久订阅者,因此它不会持久性地接收消息,而是只在运行期间接收消息。
  要想获得关于持久订阅者和Topic的更进一步的信息,请参阅Sun的JMS文档。关于上述代码的另外一点是:注意传递给该方法的第三个参数,这个参数是SELECTOR。这是消息选择器与Consumer有关联的地方(请参阅下面内容,以获得关于消息选择器的更多信息)。
  过滤——使用消息选择器
  我们将讨论的有关JMS的后一个方面是消息选择器,消息选择器是用于MessageConsumers的过滤器,可以用来过滤传入消息的属性和头文件部分(但不过滤消息体),并确定是否将实际消费该消息。按照JMS文档的说法,消息选择器是一些字符串,它们基于某种语法,而这种语法是SQL-92的子集。您可以将消息选择器作为MessageConsumer创建的一部分。根据MessageConsumer是QueueReceiver还是TopicSubscriber,这种将消息选择器应用于传入信息的方法也会稍有不同。我建议您通过检查Subscriber.java文件以及运行Publisher和Subscriber应用程序,检查消息收集器的语法,并查看如何应用它们。以下代码段在Subscriber.java中定义了消息选择器,而应用程序本身允许您从文本域中改变这个选择器:
  public final String SELECTOR = "JMSType = TOPIC_PUBLISHER";
  该选择器检查了来自Topic的传入消息的JMSType属性,并确定了这个属性的值是否等于TOPIC_PUBLISHER。如果相等,则将消息传递到MessageListener实现;如果不相等,那么消息会被忽略。请参阅“readme.txt”文件,以获得关于运行这些应用程序和展示其行为的更多细节。此外还建议您参阅Sun的JMS文档。
  结束语
  JMS是一项在应用程序中创建可移植消息传递代码的很有吸引力的、功能强大的技术。它允许进行“点对点”和“发布/订阅”消息传递,而且还支持事务和持久性。BEA的WebLogic应用服务器提供了一种健壮并完整的JMS实现,它能与应用服务器提供的其他技术一同工作,比如EJB和servlet。这为通过事务支持在不同企业对象和服务间进行的,异步的消息传递创造了极大的可能。我希望本文可以鼓励您开发在线示例代码,并且可以鼓励您检查WebLogic(尤其是JMS)提供的可能性。