在spring整合JMS的应用中咱们在定义消息监听器的时候一共能够定义三种类型的消息监听器,分别是MessageListener、SessionAwareMessageListener和MessageListenerAdapter。下面就分别来介绍一下这几种类型的区别。java
MessageListener是最原始的消息监听器,它是JMS规范中定义的一个接口。其中定义了一个用于处理接收到的消息的onMessage方法,该方法只接收一个Message参数。咱们前面在讲配置消费者的时候用的消息监听器就是MessageListener,代码以下:spring
SessionAwareMessageListener是Spring为咱们提供的,它不是标准的JMS MessageListener。MessageListener的设计只是纯粹用来接收消息的,假如咱们在使用MessageListener处理接收到的消息时咱们须要发送一个消息通知对方咱们已经收到这个消息了,那么这个时候咱们就须要在代码里面去从新获取一个Connection或Session。SessionAwareMessageListener的设计就是为了方便咱们在接收到消息后发送一个回复的消息,它一样为咱们提供了一个处理接收到的消息的onMessage方法,可是这个方法能够同时接收两个参数,一个是表示当前接收到的消息Message,另外一个就是能够用来发送消息的Session对象。先来看一段代码:apache
在上面代码中咱们定义了一个SessionAwareMessageListener,在这个Listener中咱们在接收到了一个消息以后,利用对应的Session建立了一个到destination的生产者和对应的消息,而后利用建立好的生产者发送对应的消息。数组
接着咱们在Spring的配置文件中配置该消息监听器将处理来自一个叫sessionAwareQueue的目的地的消息,而且往该MessageListener中经过set方法注入其属性destination的值为queueDestination。这样当咱们的SessionAwareMessageListener接收到消息以后就会往queueDestination发送一个消息。session
接着咱们来作一个测试,测试代码以下:app
在上述测试代码中,咱们经过前面定义好的生产者往咱们定义好的SessionAwareMessageListener监听的sessionAwareQueue发送了一个消息。程序运行以后控制台输出以下:tcp
这说明咱们已经成功的往sessionAwareQueue发送了一条纯文本消息,消息会被ConsumerSessionAwareMessageListener的onMessage方法进行处理,在onMessage方法中ConsumerSessionAwareMessageListener就是简单的把接收到的纯文本信息的内容打印出来了,以后再往queueDestination发送了一个纯文本消息,消息内容是“ConsumerSessionAwareMessageListener…”,该消息随后就被ConsumerMessageListener处理了,根据咱们的定义,在ConsumerMessageListener中也只是简单的打印了一下接收到的消息内容。工具
MessageListenerAdapter类实现了MessageListener接口和SessionAwareMessageListener接口,它的主要做用是将接收到的消息进行类型转换,而后经过反射的形式把它交给一个普通的Java类进行处理。测试
MessageListenerAdapter会把接收到的消息作以下转换:this
TextMessage转换为String对象;
BytesMessage转换为byte数组;
MapMessage转换为Map对象;
ObjectMessage转换为对应的Serializable对象。
既然前面说了MessageListenerAdapter会把接收到的消息作一个类型转换,而后利用反射把它交给真正的目标处理器——一个普通的Java类进行处理(若是真正的目标处理器是一个MessageListener或者是一个SessionAwareMessageListener,那么Spring将直接使用接收到的Message对象做为参数调用它们的onMessage方法,而不会再利用反射去进行调用),那么咱们在定义一个MessageListenerAdapter的时候就须要为它指定这样一个目标类。这个目标类咱们能够经过MessageListenerAdapter的构造方法参数指定,如:
也能够经过它的delegate属性来指定,如:
前面说了若是咱们指定的这个目标处理器是一个MessageListener或者是一个SessionAwareMessageListener的时候Spring将直接利用接收到的Message对象做为方法参数调用它们的onMessage方法。可是若是指定的目标处理器是一个普通的Java类时Spring将利用Message进行了类型转换以后的对象做为参数经过反射去调用真正的目标处理器的处理方法,那么Spring是如何知道该调用哪一个方法呢?这是经过MessageListenerAdapter的defaultListenerMethod属性来决定的,当咱们没有指定该属性时,Spring会默认调用目标处理器的handleMessage方法。
接下来咱们来看一个示例,假设咱们有一个普通的Java类ConsumerListener,其对应有两个方法,handleMessage和receiveMessage,其代码以下:
假设咱们要把它做为一个消息监听器来监听发送到adapterQueue的消息,这个时候咱们就能够定义一个对应的MessageListenerAdapter来把它当作一个MessageListener使用。
固然,有了MessageListener以后咱们还须要配置其对应的MessageListenerContainer,这里配置以下:
在上面的MessageListenerAdapter中咱们指定了其defaultListenerMethod属性的值为receiveMessage,因此当MessageListenerAdapter接收到消息以后会自动的调用咱们指定的ConsumerListener的receiveMessage方法。
针对于上述代码咱们定义测试代码以下:
这时候咱们会看到控制台输出以下:
若是咱们不指定MessageListenerAdapter的defaultListenerMethod属性,那么在运行上述代码时控制台会输出以下结果:
MessageListenerAdapter除了会自动的把一个普通Java类当作MessageListener来处理接收到的消息以外,其另一个主要的功能是能够自动的发送返回消息。
当咱们用于处理接收到的消息的方法的返回值不为空的时候,Spring会自动将它封装为一个JMS Message,而后自动进行回复。那么这个时候这个回复消息将发送到哪里呢?这主要有两种方式能够指定。
第一,能够经过发送的Message的setJMSReplyTo方法指定该消息对应的回复消息的目的地。这里咱们把咱们的生产者发送消息的代码作一下修改,在发送消息以前先指定该消息对应的回复目的地为一个叫responseQueue的队列目的地,具体代码以下所示:
接着定义一个叫responseQueue的队列目的地及其对应的消息监听器和监听容器。
ResponseQueueListener的定义以下所示:
接着把咱们接收消息的ConsumerListener的receiveMessage方法改成以下:
咱们能够看到在上述负责接收消息的receiveMessage方法有一个非空的返回值。
接着咱们运行咱们的测试代码,利用生产者往咱们定义好的MessageListenerAdapter负责处理的adapterQueue目的地发送一个消息。测试代码以下所示:
运行上述测试代码以后,控制台输出以下:
这说明咱们的生产者发送消息被MessageListenerAdapter处理以后,MessageListenerAdapter确实把监听器的返回内容封装成一个Message往原Message经过setJMSReplyTo方法指定的回复目的地发送了一个消息。对于MessageListenerAdapter对应的监听器处理方法返回的是一个null值或者返回类型是void的状况,MessageListenerAdapter是不会自动进行消息的回复的,有兴趣的网友能够本身测试一下。
第二,经过MessageListenerAdapter的defaultResponseDestination属性来指定。这里咱们也来作一个测试,首先维持生产者发送消息的代码不变,即发送消息前不经过Message的setJMSReplyTo方法指定消息的回复目的地;接着咱们在定义MessageListenerAdapter的时候经过其defaultResponseDestination属性指定其默认的回复目的地是“defaultResponseQueue”,并定义defaultResponseQueue对应的消息监听器和消息监听容器。
DefaultResponseQueueListener的代码以下所示:
这时候运行以下测试代码:
控制台将输出以下内容:
这说明MessageListenerAdapter会自动把真正的消息处理器返回的非空内容封装成一个Message发送回复消息到经过defaultResponseDestination属性指定的默认消息回复目的地。
既然咱们能够经过两种方式来指定MessageListenerAdapter自动发送回复消息的目的地,那么当咱们两种方式都指定了并且它们的目的地还不同的时候会怎么发送呢?是两个都发仍是只发其中的一个呢?关于这部分的测试我这里就不赘述了,有兴趣的网友能够本身进行。这里我能够直接的告诉你们,当两种方式都指定了消息的回复目的地的时候使用发送消息的setJMSReplyTo方法指定的目的地将具备较高的优先级,MessageListenerAdapter将只往该方法指定的消息回复目的地发送回复消息。