Sending and Receiving Messages
Java Message Service (JMS) is the core specification within Java EE and Jakarta EE for implementing asynchronous communication. It enables decoupling between applications through messages, thereby facilitating loosely coupled system architectures. Within the JMS framework, sending messages (Producer) and receiving messages (Consumer) are two of the most fundamental and critical operations. This article delves into these core concepts and demonstrates how to efficiently implement them in real-world projects using code examples.
In JMS, the object responsible for creating, encapsulating, and delivering messages is referred to as a "Producer". The primary responsibility of a Producer is to acquire resources from a connection pool, construct message objects, and send them to a specified queue or topic.
First, we need to initialize the Connection and Session objects required by JMS. Typically, we use a ConnectionFactory to obtain a Connection, and then create a Session through that connection. Depending on business requirements, you can choose a transaction mode (such as auto-commit or non-transactional), which directly impacts the reliability of message delivery.
import javax.jms.*;
import java.util.Properties;
public class JMSProducer {
public static void sendMessage() throws Exception {
// 1. Acquire ConnectionFactory and create Connection
ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Properties props = new Properties();
props.setProperty("user", "admin");
props.setProperty("password", "admin");
Connection connection = factory.createConnection();
connection.start(); // Start the connection
// 2. Create Session (using non-transactional mode here; message is acknowledged upon successful send)
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 3. Acquire target Queue
Queue queue = (Queue) session.createQueue("orders");
// 4. Create and send message
TextMessage textMessage = session.createTextMessage("Order ID: 12345, Product: Laptop");
textMessage.setText("This is a test order message");
queue.send(textMessage);
// 5. Close resources
connection.close();
}
}
In the code above, we use TextMessage to pass textual data. If the business requires passing complex objects (such as JSON, XML, or binary data), you can use types like BytesMessage or MapMessage. It is worth noting that the send method will block until the receiver has finished processing the message (depending on the ack mode). In non-transactional mode, a successful send is considered an acknowledgment, which improves throughput at the expense of some reliability.
Configuring Consumers and Receiving Messages
In contrast to Producers, objects responsible for retrieving and processing messages from queues or topics are called "Consumers". The core logic of a Consumer includes establishing a connection, subscribing to target resources, and calling the receive method to extract data.
When creating a Consumer, you also need to initialize the Connection and Session. The key distinction is that Consumers typically need to first create an implementation class for the MessageListener interface, which defines the onMessage method used to handle received message content. The JMS specification allows registering listeners on queues or topics so that callbacks are automatically triggered when new messages arrive.
import javax.jms.*;
import java.util.Properties;
public class JMSConsumer {
public static void receiveMessage() throws Exception {
// 1. Acquire ConnectionFactory and create Connection
ConnectionFactory factory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Properties props = new Properties();
props.setProperty("user", "admin");
props.setProperty("password", "admin");
Connection connection = factory.createConnection();
connection.start();
// 2. Create Session (using Client Acknowledgement mode here; Consumer decides when to send ACK)
Session session = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE);
// 3. Acquire target Queue
Queue queue = (Queue) session.createQueue("orders");
// 4. Create Listener and register to Queue
MessageListener listener = new MessageListener() {
@Override
public void onMessage(Message message) {
try {
String text = ((TextMessage) message).getText();
System.out.println("Received message: " + text);
// Processing logic...
} catch (Exception e) {
e.printStackTrace();
} finally {
// Send acknowledgment to notify the queue that the message has been processed
session.commit();
}
}
};
queue.setMessageListener(listener);
// 5. Keep connection running to listen for messages
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
connection.close();
}
}
In this example, we adopt the CLIENT_ACKNOWLEDGE mode. This means the Producer does not wait for an acknowledgment after sending a message, while the Consumer manually calls session.commit() only after successfully processing the message to notify the JMS server that the message has been consumed. This mode is ideal for high-throughput scenarios because it allows the Consumer to continue executing time-consuming operations without blocking the entire receiving thread.
Best Practices and Considerations
In actual development, properly configuring Producers and Consumers is key to ensuring stable system operation. First, pay close attention to resource leakage issues. JMS Connections and Sessions are expensive system resources and must be explicitly closed within finally blocks or try-with-resources statements to avoid exhausting the connection pool. Second, select an appropriate ack mode based on the business scenario: for financial transactions requiring high reliability, use transactional sessions; for logging or notification tasks, non-transactional modes combined with client acknowledgments often offer better efficiency. Finally, in production environments, always enable message persistence to prevent loss of sent messages that have not yet arrived after a service restart.
By mastering the core mechanisms of sending and receiving messages, developers can build more robust and scalable enterprise application architectures, fully leveraging the powerful asynchronous processing capabilities provided by JMS.