spring jms _ activemq

参考链接:
http://bsnyderblog.blogspot.com/2010/02/using-spring-jmstemplate-to-send-jms.html
http://bsnyderblog.blogspot.com/2010/02/using-spring-to-receive-jms-messages.html

Recently I stumbled upon a number of places in the some docs and mailing lists where claims are made that the Spring JmsTemplate is full of anti-patterns, is horribly inefficient and shouldn't be used. Well I'm here to debunk these erroneous claims by pointing out a class in the Spring Framework that was overlooked entirely.

The Spring JmsTemplate is a convenience class for sending and receiving JMS messages in a synchronous manner. The JmsTemplate was originally designed to be used with a J2EE container where the container provides the necessary pooling of the JMS resources (i.e., connections, consumers and producers). Such requirements came from the EJB spec. But when developers began using the JmsTemplate outside of J2EE containers, and because some JMS providers do not offer caching/pooling of JMS resources, a different solution was necessary. Enter the Spring CachingConnectionFactory.

The CachingConnectionFactory is meant to wrap a JMS provider's connection to provide caching of sessions, connections and producers as well as automatic connection recovery. By default, it uses a single session to create many connections and this model works very well with most MOMs. But if you need to scale further, you can also specify the number of sessions to cache using the sessionCacheSize property.

Below is a snippet from a Spring app context that demonstrates the configuration for the CachingConnectionFactory

01
...
02

03
<!-- A connection to ActiveMQ -->
04

05
<bean id="amqConnectionFactory"
06

07
    class="org.apache.activemq.ActiveMQConnectionFactory"
08

09
    p:brokerURL='tcp://localhost:61616" />
10

11

12

13
<!-- A cached connection to wrap the ActiveMQ connection -->
14

15
<bean id="cachedConnectionFactory"
16

17
    class="org.springframework.jms.connection.CachingConnectionFactory"
18

19
    p:targetConnectionFactory-ref="amqConnectionFactory"
20

21
    p:sessionCacheSize="10" />
22

23

24

25
<!-- A destination in ActiveMQ -->
26

27
<bean id="destination"
28

29
    class="org.apache.activemq.command.ActiveMQQueue">
30

31
<constructor-arg value="FOO.TEST" />
32

33
</bean>
34

35

36

37
<!-- A JmsTemplate instance that uses the cached connection and destination -->
38

39
<bean id="producerTemplate"
40

41
    class="org.springframework.jms.core.JmsTemplate"
42

43
    p:connectionFactory-ref="cachedConnectionFactory"
44

45
    p:defaultDestination-ref="destination" />
46

47
...


As you can see, the configuration for the CachingConnectionFactory along with the JmsTemplate is quite simple. Furthermore, these two classes are also both in the org.springframework.jms package path so they're both included in the spring-jms jar file making their use even easier.

The only thing left to do is utilize the jmsTemplate bean in your Java code to actually send a message. This is shown below:

01
public class SimpleMessageProducer {
02

03
    
04

05
    private static final Logger LOG = Logger.getLogger(SimpleMessageProducer.class);
06

07
    
08

09
    @Autowired
10

11
    protected JmsTemplate jmsTemplate;
12

13
    
14

15
    protected int numberOfMessages = 100;
16

17
    
18

19
    public void sendMessages() throws JMSException {
20

21
        StringBuilder payload = null;
22

23
        
24

25
        for (int i = 0; i < numberOfMessages; ++i) {
26

27
            
28

29
            payload = new StringBuilder();
30

31
            payload.append("Message [").append(i).append("] sent at: ").append(new Date());
32

33
            
34

35
            jmsTemplate.send(new MessageCreator() {
36

37
                public Message createMessage(Session session) throws JMSException {
38

39
                    TextMessage message = session.createTextMessage(payload.toString());
40

41
                    message.setIntProperty("messageCount", i);
42

43
                    LOG.info("Sending message number [" + i + "]");
44

45
                    return message;
46

47
                }
48

49
            });
50

51
        }
52

53
    }
54

55
}


The SimpleMessageProducer class above demonstrates the use of Spring autowiring to resolve the relationship between the jmsTemplate property and the producerTemplate in the app context further above. Then an anonymous MessageCreator instance is used to actually create a message for the jmsTemplate to send.

The JmsTemplate and the CachingConnectionFactory are both very widely used in businesses of all sizes throughout the world. Coupled with one of the Spring message listener containers, they provide an ideal solution.

I'll elaborate on message consumption using the Spring DefaultMessageListenerContainer and the SimpleMessageListenerContainer in a future blog post.


Have you ever had a need to create your own JMS consumer? Or will you have this need in the future? If you answered yes to either one of these questions, this post will simplify your life.

In the previous post, I discussed Using the Spring JmsTemplate to Send JMS Messages. As a follow-on, in this post I will demonstrate how to receive messages using Spring JMS. Although the previously mentioned JmsTemplate can receive messages synchronously, here I will focus on asynchronous message reception using the Spring message listener container architecture, specifically the DefaultMessageListenerContainer.

The DefaultMessageListenerContainer (DMLC) is another wonderful convenience class that is part of the Spring Framework's JMS package. As you can see in the Javadoc, the DMLC is not a single class, but a well-abstracted hierarchy for the purpose of receiving messages. The reason for this is that the DMLC takes its inspiration from Message Driven Beans (MDB).

MDBs were originally defined in the EJB 2.0 spec as a stateless, transaction aware message listener that use JMS resources provided by the Java EE container. MDBs can also be pooled by the Java EE container in order to scale up. In short, MDBs were designed for asynchronous message reception in a way that the Java EE container could manage them. Although the intention was good, unfortunately the disadvantages of MDBs are numerous including:
MDBs are static in their configuration and creation (they cannot be created dynamically)
MDBs can only listen to a single destination
MDBs can only send messages after first receiving a message
MDBs require an EJB container (and therefore the Java EE container)
Although the Spring DMLC took its inspiration from MDBs, it did not replicate these disadvantages; quite the opposite, in fact. The Spring DMLC is commonly used to create what have become known as Message-Driven POJOs (MDP). MDPs offer all of the same functionality as MDBs but without the disadvantages listed above. The Spring DMLC provides many features including:
Various levels of caching of the JMS resources (connections and sessions) and JMS consumers for increased performance
The ability to dynamically grow and shrink the number of consumers to concurrently process messages based on load (see setConcurrentConsumers and setMaxConcurrentConsumers) for additional performance
Automatically re-establishes connections if the message broker becomes unavailable
Asynchronous execution of a message listener using the Spring TaskExecutor
Support for local JMS transactions as well as an external transaction manager around message reception and listener execution
Support for various message acknowledgement modes, each providing different semantics
For some situations, it is important to understand the additional error handling and the redelivery semantics that are provided by the DMLC. For more information, see the AbstractMessageListenerContainer JavaDoc.

The reason I recommend the DMLC (or even the SimpleMessageListenerContainer) is because writing JMS consumers can be a lot of work. In doing so, you must manually handle and mange the JMS resources and the JMS consumers, any concurrency that is necessary and any use of transactions. If you've ever done such work you know how arduous and error prone it can be. Certainly MDBs provide some of these features but with all their disadvantages. By creating MDPs using the Spring DMLC, I have seen users save a tremendous amount of time and increase their productivity significantly. This is because the DMLC offers much flexibility, robustness, a high amount of configurability and it has widespread deployment in businesses all over the world (so it has been widely tested).

Compared to MDBs, use of the Spring DMLC is actually surprisingly simple. The easiest way to get started is to using an XML configuration as the Spring DMLC provides JMS namespace support. Below is a Spring application context that demonstrates the configuration to use the Spring DMLC with Apache ActiveMQ:

01
<?xml version="1.0" encoding="UTF-8"?>
02
<beans xmlns="http://www.springframework.org/schema/beans"
03
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
04
       xmlns:jms="http://www.springframework.org/schema/jms"
05
       xmlns:p="http://www.springframework.org/schema/p"
06
       xsi:schemaLocation="
07
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
08
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd">
09

10
  <!-- A JMS connection factory for ActiveMQ -->
11
  <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
12
  p:brokerURL="tcp://foo.example.com:61616" />
13

14
  <!-- A POJO that implements the JMS message listener -->
15
  <bean id="simpleMessageListener" class="com.mycompany.SimpleMessageListener">
16

17
  <!-- The Spring message listener container configuration -->
18
  <jms:listener-container
19
      container-type="default"
20
      connection-factory="connectionFactory"
21
      acknowledge="auto">
22
    <jms:listener destination="TEST.FOO" ref="simpleMessageListener" method="onMessage" />
23
  </jms:listener-container>
24
</beans>


For folks who are already familiar with the Spring Framework, the XML above is quite straightforward. It defines a connection factory bean for ActiveMQ, a message listener bean and the Spring listener-container. Notice that the jms:listener contains the destination name and not the listener-container. This level of separation is important because it means that the listener-container is not tied to any destination, only the jms:listener is. You can define as many jms:listener elements as is necessary for your application and the container will handle them all.

Below is the message listener implementation:

01
import javax.jms.JMSException;
02
import javax.jms.Message;
03
import javax.jms.MessageListener;
04
import javax.jms.TextMessage;
05

06
import org.apache.log4j.Logger;
07

08
public class MyMessageListener implements MessageListener {
09

10
  private static final Logger LOG = Logger.getLogger(MyMessageListener.class);
11

12
  public void onMessage(Message message) {
13
      try {
14
       TextMessage msg = (TextMessage) message;
15
       LOG.info("Consumed message: " + msg.getText());
16
      } catch (JMSException e) {
17
          // TODO Auto-generated catch block
18
          e.printStackTrace();
19
      }
20
  }
21

22
}

The message listener implementation is deliberately simple as its only purpose is to demonstrate receiving the message and logging the payload of the message. Although this listener implements the javax.jms.MessageListener interface, there are a total of three options available for implementing a message listener to be used with the Spring DMLC:
The javax.jms.MessageListener - This is what was used in the example above. It is a standardized interface from the JMS spec but handling threading is up to you.
The Spring SessionAwareMessageListener - This is a Spring-specific interface the provides access to the JMS session object. This is very useful for request-response messaging. Just be aware that you must do your own exception handling (i.e., override the handleListenerException method so exceptions are not lost).
The Spring MessageListenerAdapter - This is a Spring-specific interface that allows for type-specific message handling. Use of this interface avoids any JMS-specific dependencies in your code.


So not only is the Spring message listener container easy to use, it is also full of options to adapt to many environments. And I've only focused on the DefaultMessageListenerContainer here, I have not talked about the SimpleMessageListenerContainer (SMLC) beyond a simple mention. At a high level the difference is that the SMLC is static and provides no support for transactions.

One very big advantage of the Spring message listener container is that this type of XML config can be used in a Java EE container, in a servlet container or stand alone. This same Spring application context will run in Weblogic, JBoss, Tomcat or in a stand alone Spring container. Furthermore, the Spring DMLC also works with just about any JMS compliant messaging middleware available. Just define a bean for the JMS connection factory for your MOM and possibly tweak a few properties on the listener-container and you can begin consuming messages from different MOMs.

I should also note that the XML configuration is certainly not a requirement either. You can go straight for the underlying Java classes in your own code if you wish. I've used each style in various situations, but to begin using the Spring DMLC in the shortest amount of time, I find the Spring XML application context the fastest.

你可能感兴趣的:(activemq)