Spring integrates JMS - based on ActiveMQ implementation


zhuan zi: http://blog.csdn.net/haoxingfeng/article/details/9167895
Spring integrates JMS - based on ActiveMQ implementation (1)
1.1 Introduction
       to JMS The full name of JMS is Java Message Service, which is Java Message Service. It is mainly used for message passing between producers and consumers, where producers are responsible for producing messages and consumers are responsible for receiving messages. If it is applied to the actual business requirements, we can use the producer to generate a message at a specific time and send it, and the corresponding consumer will complete the corresponding business logic after receiving the corresponding message. There are two types of message delivery, one is point-to-point, that is, a producer and a consumer correspond one-to-one; the other is publish/subscribe mode, that is, after a producer generates a message and sends it, it can be sent by multiple consumers to receive.
1.2 Spring integrates JMS
       After a brief introduction to JMS, let's talk about the specific process of Spring's integration of JMS. JMS is just a standard. When we really use it, we need to have its specific implementation. Here we use Apache's activeMQ as its implementation. The dependencies used are managed by Maven, and the specific dependencies are as follows:

Xml code 
<dependencies> 
        <dependency> 
            <groupId>junit</groupId> 
            <artifactId>junit</artifactId> 
            <version>4.10</version> 
            <scope>test</scope> 
        </dependency> 
        <dependency> 
            <groupId>org.springframework</groupId> 
            <artifactId>spring-context</artifactId> 
            <version>${spring-version}</version> 
        </dependency> 
        <dependency> 
            <groupId>org.springframework</groupId> 
            <artifactId>spring-jms</artifactId> 
            <version>${spring-version}</version> 
        </dependency> 
        <dependency> 
            <groupId>org.springframework</groupId> 
            <artifactId>spring-test</artifactId> 
            <version>${spring-version}</version> 
        </dependency> 
        <dependency> 
            <groupId>javax.annotation</groupId> 
            <artifactId>jsr250-api</artifactId> 
            <version>1.0</version> 
        </dependency> 
        <dependency> 
            <groupId>org.apache.activemq</groupId> 
            <artifactId>activemq-core</artifactId> 
            <version>5.7.0</version> 
        </dependency> 
</dependencies> 

<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring-version}</version> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>jsr250-api</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-core</artifactId> <version>5.7.0</version> </dependency></dependencies>

1.2.1 activeMQ preparation
       Since apache's activeMQ is used as the implementation of JMS, first we should download activeMQ (http://activemq.apache.org/download.html) from the apache official website, decompress it and run its bin directory The following activemq.bat file starts activeMQ.
1.2.2 Configuring ConnectionFactory
       ConnectionFactory is used to generate links to the JMS server. Spring provides us with multiple ConnectionFactory, including SingleConnectionFactory and CachingConnectionFactory. SingleConnectionFactory will always return the same connection for requests to establish a JMS server connection, and will ignore the Connection's close method call. CachingConnectionFactory inherits SingleConnectionFactory, so it has all the functions of SingleConnectionFactory, and it also adds a caching function, which can cache Session, MessageProducer and MessageConsumer. Here we use SingleConnectionFactory as an example.
Xml code 
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"/> 

<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"/>

       Does this define the ConnectionFactory that generates the JMS server connection? The answer is yes and no. The ConnectionFactory provided by Spring is only used by Spring to manage the ConnectionFactory. The ConnectionFactory that actually generates the link to the JMS server must be provided by the JMS service provider, and it needs to be injected into the ConnectionFactory provided by Spring. What we use here is JMS implemented by ActiveMQ, so what we can really generate Connection here should be the ConnectionFactory provided by ActiveMQ. So the complete code to define a ConnectionFactory should be as follows:
Xml code 
<!-- The ConnectionFactory that can really generate Connection, provided by the corresponding JMS service vendor --> 
<bean id="targetConnectionFactory" class="org.apache.activemq .ActiveMQConnectionFactory"> 
    <property name="brokerURL" value="tcp://localhost:61616"/> 
</bean> 
 
<! 
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"> 
    <!-- The target ConnectionFactory corresponds to the real ConnectionFactory that can generate JMS Connection --> 
    <property name="targetConnectionFactory" ref="targetConnectionFactory "/> 
</bean> 

<!-- The ConnectionFactory that can actually generate Connection, provided by the corresponding JMS service vendor --> <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name ="brokerURL" value="tcp://localhost:61616"/> </bean> <!-- Spring's ConnectionFactory to manage the real ConnectionFactory --> <bean id="connectionFactory" class="org.springframework .jms.connection.SingleConnectionFactory"> <! -- The target ConnectionFactory corresponds to the real ConnectionFactory that can generate JMS Connection --> <property name="targetConnectionFactory" ref="targetConnectionFactory"/> </bean>
 
1.2.3 Configure the producer After
configuring the ConnectionFactory, we need to configure the producer. The producer is responsible for generating the message and sending it to the JMS server, which usually corresponds to one of our business logic service implementation classes. But how does our service implementation class send messages? This is usually achieved by using the JmsTemplate class provided by Spring, so the core of configuring the producer is to configure the JmsTemplate for message sending. For the message sender, it needs to know where to send the message when it sends the message. For this reason, when we define the JmsTemplate, we need to inject a ConnectionFactory object provided by Spring into it.
Xml code 
<!-- JMS tool class provided by Spring, it can send and receive messages, etc.--> 
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> 
    <!-- this The connectionFactory corresponds to the ConnectionFactory object provided by Spring we defined --> 
    <property name="connectionFactory" ref="connectionFactory"/> 
</bean> 

<!-- The JMS tool class provided by Spring, which can send and receive messages, etc.--> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <!-- This connectionFactory corresponds to What we define is the ConnectionFactory object provided by Spring --> <property name="connectionFactory" ref="connectionFactory"/> </bean>

       When we really use JmsTemplate to send messages, we need to know the destination of the message , the destination. There is a Destination interface used to represent the destination in Jms. It does not have any method definition, but is only used as an identifier. When we do not specify the destination when using JmsTemplate for message sending, the default Destination will be used. The default Destination can be injected through the attribute defaultDestination or defaultDestinationName when defining the jmsTemplate bean object, and defaultDestinationName corresponds to a common string. Two types of Destinations are implemented in ActiveMQ, one is the point-to-point ActiveMQQueue, and the other is the ActiveMQTopic that supports the subscription/publishing mode. When defining these two types of Destinations, we can construct them through a name attribute, such as:




Xml code 
<!--This is the queue destination, point-to-point --> 
<bean id=" 
    <constructor-arg> 
        <value>queue</value> 
    </constructor-arg> 
</bean> 
<!--This is the topic destination, one-to-many--> 
<bean id="topicDestination" class=" org.apache.activemq.command.ActiveMQTopic"> 
    <constructor-arg value="topic"/> 
</bean> 

<!--This is the queue destination, point-to-point --> <bean id="queueDestination" class= "org.apache.activemq.command.ActiveMQQueue"> <constructor-arg> <value>queue</value> </constructor-arg> </bean> <!--This is the topic destination, one-to-many- -> <bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic"> <constructor-arg value="topic"/> </bean>



       Suppose we define a ProducerService with a method sendMessage that sends plain text messages to Destination, then our code looks like this:




Java code 
package com.tiantian.springintejms.service.impl;  
   
import javax.annotation.Resource;  
import javax.jms.Destination;  
import javax.jms.JMSException;  
import javax.jms.Message;  
import javax.jms.Session;  
   
import org.springframework.jms.core.JmsTemplate;  
import org.springframework.jms.core.MessageCreator;  
import org.springframework.stereotype.Component;  
   
import com.tiantian.springintejms.service.ProducerService;  
   
@Component 
public class ProducerServiceImpl implements ProducerService {  
   
    private JmsTemplate jmsTemplate;  
      
    public void sendMessage(Destination destination, final String message) {  
        System.out.println("---------------生产者发送消息-----------------");  
        System.out.println("---------------生产者发了一个消息:" + message);  
        jmsTemplate.send(destination, new MessageCreator() {  
            public Message createMessage(Session session) throws JMSException {  
                return session.createTextMessage(message);  
            }  
        });  
    }   
 
    public JmsTemplate getJmsTemplate() {  
        returnjmsTemplate;  
    }   
 
    @Resource 
    public void setJmsTemplate(JmsTemplate jmsTemplate) {  
        this.jmsTemplate = jmsTemplate;  
    }  
   


package com.tiantian.springintejms.service.impl; import javax.annotation.Resource;import javax.jms.Destination;import javax.jms.JMSException;import javax.jms.Message;import javax.jms.Session; import org.springframework.jms.core.JmsTemplate;import org.springframework.jms.core.MessageCreator;import org.springframework.stereotype.Component; import com.tiantian.springintejms.service.ProducerService; @Componentpublic class ProducerServiceImpl implements ProducerService { private JmsTemplate jmsTemplate; public void sendMessage(Destination destination, final String message) { System.out.println("---------------生产者发送消息-----------------"); System.out.println("---------------生产者发了一个消息:" + message); jmsTemplate.send(destination, new MessageCreator() { public Message createMessage(Session session) throws JMSException { return session.createTextMessage(message); } }); } public JmsTemplate getJmsTemplate() { returnjmsTemplate; } @Resource public void setJmsTemplate(JmsTemplate jmsTemplate) { this.jmsTemplate = jmsTemplate; } }



       We can see that in the body of the sendMessage method, we send messages to the corresponding Destination through jmsTemplate. At this point, we are configured to generate a simple text message and send it to the producer of the specified destination Destination.
1.2.4 Configure the consumer After the
producer sends a message to the specified destination Destination, the next step is for the consumer to consume the message from the specified destination. So how does the consumer know that a producer has sent a message to the specified destination Destination? This is achieved through the MessageListenerContainer, a message listener container encapsulated by Spring, which is responsible for receiving information and distributing the received information to the real MessageListener for processing. Each consumer needs a corresponding MessageListenerContainer for each destination. For the message monitoring container, in addition to knowing which destination to listen to, it also needs to know where to listen, that is to say, it also needs to know which JMS server to listen to. This is done by injecting a message when configuring the MessageConnectionFactory. ConnectionFactory to achieve. So when we configure a MessageListenerContainer, there are three properties that must be specified, one is the ConnectionFactory that indicates where to listen from; the other is the Destination that indicates what to listen for; the other is the MessageListener that processes the message after receiving the message. Spring provides us with two types of MessageListenerContainer, SimpleMessageListenerContainer and DefaultMessageListenerContainer.
SimpleMessageListenerContainer will create a session and consumer Consumer at the beginning, and will use the standard JMS MessageConsumer.setMessageListener() method to register the listener to let the JMS provider call the listener's callback function. It does not dynamically adapt to runtime needs and participate in external transaction management. Compatibility-wise, it is very close to the stand-alone JMS specification, but generally not compatible with the JMS limitations of Java EE.
In most cases, we still use DefaultMessageListenerContainer. Compared with SimpleMessageListenerContainer, DefaultMessageListenerContainer dynamically adapts to runtime needs and can participate in external transaction management. It is a good balance of low requirements on the JMS provider, advanced features such as transaction participation and compatibility with the Java EE environment.
Defining a MessageListener
       that handles messages To define a MessageListener that handles messages, we only need to implement the MessageListener interface in the JMS specification. There is only one method in the MessageListener interface, the onMessage method, which is automatically called when a message is received.




Java code 
package com.tiantian.springintejms.listener;  
   
import javax.jms.JMSException;  
import javax.jms.Message;  
import javax.jms.MessageListener;  
import javax.jms.TextMessage;  
   
public class ConsumerMessageListener implements MessageListener {  
   
    public void onMessage(Message message) {  
        //Here we know that what the producer sends is a plain text message, so we can directly cast it here, or directly change the parameters of the onMessage method to a subclass of Message TextMessage 
        TextMessage textMsg = (TextMessage) message;  
        System.out.println("A plain text message was received.");  
        try {  
            System.out.println("The content of the message is: " + textMsg.getText());  
        } catch (JMSException e) {  
            e.printStackTrace();  
        }  
    }  
   


package com.tiantian.springintejms.listener; import javax.jms.JMSException;import javax.jms.Message;import javax.jms.MessageListener;import javax.jms.TextMessage; public class ConsumerMessageListener implements MessageListener { public void onMessage(Message message) { //Here we know that what the producer sends is a plain text message, so we can directly perform forced conversion here, or directly change the parameter of the onMessage method to a subclass of Message TextMessage TextMessage textMsg = (TextMessage) message; System.out. println("A plain text message was received."); try { System.out.println("The message content is: " + textMsg.getText()); } catch (JMSException e) { e.printStackTrace(); } } }

 

       With MessageListener, we can configure a message listener container in Spring's configuration file.
Xml code 
<!--This is the queue destination--> 
<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue"> 
    <constructor-arg> 

    </constructor-arg> 
</bean> 
<!-- 消息监听器 --> 
<bean id="consumerMessageListener" class="com.tiantian.springintejms.listener.ConsumerMessageListener"/>      
 
<!-- 消息监听容器 --> 
<bean id="jmsContainer"        class="org.springframework.jms.listener.DefaultMessageListenerContainer"> 
    <property name="connectionFactory" ref="connectionFactory" /> 
    <property name="destination" ref="queueDestination" /> 
    <property name="messageListener" ref="consumerMessageListener" /> 
</bean> 








<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:jms="http://www.springframework.org/schema/jms" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
     http://www.springframework.org/schema/context  
     http://www.springframework.org/schema/context/spring-context-3.0.xsd  
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
    http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd"> 
   
    <context:component-scan base-package="com.tiantian" " /> 
   
    <!-- The JMS tool class provided by Spring, which can send and receive messages --> 
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> 
        <!-- This connectionFactory corresponds to the ConnectionFactory object provided by Spring that we defined --> 
        <property name="connectionFactory" ref="connectionFactory"/> 
    </bean> 
      
    <!-- The ConnectionFactory that can really generate Connection, by the corresponding JMS Service provider provides --> 
    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">   
        <property name="brokerURL" value="tcp://localhost:61616"/> 
    </bean> 
      
    <!-- The ConnectionFactory that Spring uses to manage the real ConnectionFactory --> 
    <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"> 
        <!-- The target ConnectionFactory corresponds to the real JMS Connection that can be generated ConnectionFactory --> 
        <property name="targetConnectionFactory" ref="targetConnectionFactory"/> 
    </bean> 
      
    <!--this is the queue destination --> 
    <bean id="queueDestination" class="org.apache.activemq .command.ActiveMQQueue"> 
        <constructor-arg> 
            <value>queue</value> 
        </constructor-arg> 
    </bean> 
    <!-- message listener -->   
    <bean id="consumerMessageListener" class="com.tiantian.springintejms.listener.ConsumerMessageListener"/> 
    <!-- 消息监听容器 --> 
    <bean id="jmsContainer" 
        class="org.springframework.jms.listener.DefaultMessageListenerContainer"> 
        <property name="connectionFactory" ref="connectionFactory" /> 
        <property name="destination" ref="queueDestination" /> 
        <property name="messageListener" ref="consumerMessageListener" /> 
    </bean> 
</beans> 

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:jms="http://www.springframework.org/schema/jms" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd"> <context:component-scan base-package="com.tiantian" /> <!-- The JMS tool class provided by Spring, which can send and receive messages --> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <!- - This connectionFactory corresponds to the ConnectionFactory object provided by Spring that we defined--> <property name="connectionFactory" ref="connectionFactory"/> </bean> <!-- The ConnectionFactory that can really generate Connection, by the corresponding JMS service provider provides --> <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616"/> </bean> <!-- The ConnectionFactory that Spring uses to manage the real ConnectionFactory --> <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"> <!-- The target ConnectionFactory corresponds to the real ConnectionFactory that can generate JMS Connection --> <property name="targetConnectionFactory" ref="targetConnectionFactory"/> </bean> <!--This is the queue destination --> <bean id= "queueDestination" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg> <value>queue</value> </constructor-arg> </bean> <!-- message listener --> <bean id="consumerMessageListener" class="com.tiantian.springintejms.listener.ConsumerMessageListener"/> <!-- Message listener container--> <bean id="jmsContainer" class="org.springframework.jms.listener. DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory" /> <property name="destination" ref="queueDestination" /> <property name="messageListener" ref="consumerMessageListener" /> </bean></beans>


       Then let's test to see if our integration is really successful. The test code is as follows:
Java code 
package com.tiantian.springintejms.test;  
   
import javax.jms.Destination;  
   
import org.junit.Test;  
import org.junit .runner.RunWith;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.beans.factory.annotation.Qualifier;  
import org.springframework.test.context.ContextConfiguration;  
import org.springframework.test.context .junit4.SpringJUnit4ClassRunner;  
import com.tiantian.springintejms.service.ProducerService;  
   
@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration("/applicationContext.xml")  
public class ProducerConsumerTest {  
   
    @Autowired 
    private ProducerService producerService;  
    @Autowired 
    @Qualifier("queueDestination")  
    private Destination destination;  
      
    @Test 
    public void testSend() {  
        for (int i=0; i<2; i++) {  
            producerService.sendMessage(destination, "你好,生产者!这是消息:" + (i+1));  
        }  
    }  
      


package com.tiantian.springintejms.test; import javax.jms.Destination; import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import com.tiantian.springintejms.service.ProducerService; @RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration("/applicationContext.xml")public class ProducerConsumerTest { @Autowired private ProducerService producerService; @Autowired @Qualifier("queueDestination") private Destination destination; @Test public void testSend() { for (int i=0; i<2; i++) { producerService.sendMessage(destination, "你好,生产者!这是消息:" + (i+1)); } } }



       In the above test code, we use the producer to send two messages. Normally, the consumer should be able to receive these two messages. After running the test code, the console output is as follows:



       Look, the console has produced the correct output, which shows that our integration has indeed been successful.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326487903&siteId=291194637