17.2.3 创建消息驱动的 POJO
在学校时的一个暑假期间,我得到了在黄石国家公园工作的机会。这个工作并不是公园巡逻者或者开关老忠实泉(Old Faithful)这样的高级工作,而是在老忠实泉酒店进行更换床单、清理卫生间以及打扫地板等家务工作。虽然不是很吸引人,但至少我是在这个世界上最美丽的地方工作。
每天工作之后,我都到当地的邮局看看是否有我的邮件。我已经离家好几个星期了,所以能收到学校朋友的来信或者明信片是一件非常美好的事情。我没有自己的邮箱,所以必须走着去邮局,并询问坐在柜台后的工作人员是否有我的邮件。接着就是开始等待。
要知道,柜台后的那个人大约有195岁了。像他这个岁数的人,走动起来很费时间。他从椅子上站起来,慢慢走过地板,消失在隔墙后。过了一会儿,他出现了,慢慢回到柜台,坐到椅子上,然后看着我说:“今天没有邮件”。
JmsTemplate 的 receive() 方法与这个上了年纪的邮局雇员很像。当我们调用 receive() 方法时,JmsTemplate 会查看队列或主题中是否有消息,直到收到消息或者等待超时才会返回。这期间,应用无法处理任何事情,只能等待是否有消息。如果应用能够继续进行其他业务处理,当消息到达时再去通知它,不是更好吗?
EJB2 规范的一个重要内容是引入了消息驱动 bean(message-driven bean,MDB)。MDB 是可以异步处理消息的 EJB。换句话说,MDB 将 JMS 目的地中的消息作为事件,并对这些事件进行响应。而与之相反的是,同步消息接收者在消息可用前会一直处于阻塞状态。
MDB 是 EJB 中的一个亮点。即使那些狂热的 EJB 反对者也认为 MDB 可以优雅地处理消息。EJB 2 MDB 的唯一缺点是它们必须要实现 java.ejb.MessageDrivenBean。此外,它们还必须实现一些 EJB 生命周期的回调方法。简而言之,EJB 2 MDB 不是纯的 POJO。
在 EJB 3 规范中,MDB 进一步简化了,使其更像 POJO。我们不再需要实现 MessageDrivenBean 接口,而是实现更通用的 javax.jms.MessageListener 接口,并使用 @MessageDriven 注解标注 MDB。
Spring 2.0 提供了它自己的消息驱动 bean 来满足异步接收消息的需求,这种形式与 EJB 3 的 MDB 很相似。在本节中,我们将学习到 Spring 是如何使用消息驱动 POJO(我们将其简称为 MDP)来支持异步接收消息的。
创建消息监听器
如果使用 EJB 的消息驱动模型来创建 Spittle 的提醒处理器,我们需要使用 @MessageDriven 注解进行标注。即使它不是严格要求的,但 EJB 规范还是建议 MDB 实现 MessageListener 接口。Spittle 的提醒处理器最终可能是这样的:
想象一下,如果消息驱动组件不需要实现 MessageListener 接口,世界将是多么的简单。在这里,天是蔚蓝的,鸟儿唱着我们喜欢的歌,我们不再需要实现 onMessage() 方法或者注入 MessgeDrivenContext。
好吧,可能 EJB 3 规范所要求的 MDB 也算不上太麻烦。但是事实上,SpittleAlertHandler 的 EJB 3 实现太依赖于 EJB 的消息驱动 API,并不是我们所希望的 POJO。理想情况下,我们希望提醒处理器能够处理消息,但是不用编码,就好像它知道应该做什么。
Spring 提供了以 POJO 的方式处理消息的能力,这些消息来自于 JMS 的队列或主题中。例如,基于 POJO 实现 SpittleAlertHandler 就足以做到这一点。
虽然改变天空的颜色和训练鸟儿歌唱超出了 Spring 的范围,但程序清单 17.5 所展示的现实与我描绘的理想世界非常接近。我们稍后会编写 handleSpittleAlert() 方法的具体内容。现在,程序清单 17.5 所展示的 SpittleAlertHandler 没有任何 JMS 的痕迹。从任意一个 角度观察,它都是一个纯粹的 POJO。它仍然可以像 EJB 那样处理消息,只不过它还需要一些 Spring 的配置。
配置消息监听器
为 POJO 赋予消息接收能力的诀窍是在 Spring 中把它配置为消息监听器。Spring 的 jms 命名空间为我们提供了所需要的一切。首先,让我们先把处理器声明为 bean:
然后,为了把 SpittleAlertHandler 转变为消息驱动的 POJO,我们需要把这个 bean 声明为消息监听器:
在这里,我们在消息监听器容器中包含了一个消息监听器。消息监听器容器(message listener container)是一个特殊的 bean,它可以监控 JMS 目的地并等待消息到达。一旦有消息到达,它取出消息,然后把消息传给任意一个对此消息感兴趣的消息监听器。如图 17.7 展示了 这个交互过程。
为了在 Spring 中配置消息监听器容器和消息监听器,我们使用了 Spring jms 命名空间中的两个元素。 <jms:listener-container> 中包含了 <jms:listener> 元素。这里的 connection-factory 属性配置了对 connectionFactory 的引用,容器中的每个 <jms:listener> 都使用这个连接工厂进行消息监听。在本示例中,connection-factory 属性可以移除,因为该属性的默认值就是 connectionFactory。
对于 <jms:listener> 元素,它用于标识一个 bean 和一个可以处理消息的方法。为了处理 Spittle 提醒消息,ref 元素引用了 spittleHandler bean。当消息到达 spitter.alert.queue 队列 (通过 destination 属性配置)时,spittleHandlerbean 的 handleSpittleAlert() 方法(通过 method 属性指定的)会被触发。
值得一提的是,如果 ref 属性所标示的 bean 实现了 MessageListener,那就没有必要再指定 method 属性了,默认就会调用 onMessage() 方法。
Last updated