JmsClient vs JmsTemplate: When to Use Spring 7.0's New JMS API
Purpose
Spring 7.0 introduced JmsClient, a modern fluent API for sending JMS messages. If you’re like me, you’ve been using JmsTemplate for years and might wonder: should I switch? This post will help you understand the differences and make the right choice for your project.
What is JmsTemplate?
JmsTemplate has been the go-to API for JMS operations in Spring since the early days. It provides a template method pattern for all JMS operations: sending, receiving, and browsing messages.
@Servicepublic class OrderService {
@Autowired private JmsTemplate jmsTemplate;
public void sendOrder(Order order) { jmsTemplate.send("order.queue", session -> { ObjectMessage message = session.createObjectMessage(order); message.setJMSCorrelationID(order.getId()); return message; }); }
public Order receiveOrder() { return (Order) jmsTemplate.receiveAndConvert("order.queue"); }}The JmsTemplate approach works well, but it can feel verbose for simple send operations. The template method pattern requires you to provide a callback for message creation, which adds boilerplate code.
What is JmsClient?
JmsClient is Spring 7.0’s new fluent API designed specifically for sending messages. It offers a cleaner, chainable syntax that reduces boilerplate code.
@Servicepublic class OrderService {
@Autowired private JmsClient jmsClient;
public void sendOrder(Order order) { jmsClient.send(order) .to("order.queue") .withCorrelationId(order.getId()) .execute(); }}You can see the difference immediately. The fluent API reads naturally from left to right, making it clear what’s happening: send an order to the order queue with a correlation ID.
Feature Comparison
Let me show you a detailed comparison between the two APIs:
| Feature | JmsClient | JmsTemplate |
|---|---|---|
| Send messages | Yes - Fluent API | Yes - Template method |
| Receive messages | No | Yes |
| Browse messages | No | Yes |
| Batch operations | No | Yes |
| Fluent chaining | Yes | No |
| Message conversion | Automatic | Automatic |
| Transaction support | Yes | Yes |
Code Comparison: Sending Messages
Let me show you more examples comparing how both APIs handle common scenarios.
Sending a Simple Text Message
With JmsTemplate:
jmsTemplate.send("notifications.queue", session -> { TextMessage message = session.createTextMessage("Order shipped!"); return message;});With JmsClient:
jmsClient.send("Order shipped!") .to("notifications.queue") .execute();Sending an Object with Custom Properties
With JmsTemplate:
jmsTemplate.convertAndSend("order.queue", order, message -> { message.setStringProperty("orderType", order.getType()); message.setIntProperty("priority", order.getPriority()); message.setJMSCorrelationID(order.getId()); return message;});With JmsClient:
jmsClient.send(order) .to("order.queue") .withProperty("orderType", order.getType()) .withProperty("priority", order.getPriority()) .withCorrelationId(order.getId()) .execute();The JmsClient version is more readable and requires less nesting.
Receiving Messages: JmsTemplate Only
Here’s where JmsTemplate still shines. JmsClient cannot receive messages, so if you need to consume messages from a queue, you must use JmsTemplate.
@Servicepublic class OrderProcessor {
@Autowired private JmsTemplate jmsTemplate;
public Order receiveOrder() { return (Order) jmsTemplate.receiveAndConvert("order.queue"); }
public Order receiveOrderWithTimeout(long timeout) { return (Order) jmsTemplate.receiveSelectedAndConvert( "order.queue", "priority > 5", timeout ); }
public void browseOrders() { jmsTemplate.browse("order.queue", (session, browser) -> { Enumeration<Message> messages = browser.getEnumeration(); while (messages.hasMoreElements()) { Message message = messages.nextElement(); // Process browsed message } return null; }); }}Spring Boot Auto-Configuration
Spring Boot automatically configures both JmsClient and JmsTemplate beans when you have JMS on your classpath.
@Configurationpublic class JmsConfig {
@Bean public MessageConverter orderMessageConverter() { return new MappingJackson2MessageConverter(); }}The MessageConverter bean is automatically shared between both APIs, so you don’t need to configure it twice.
Common Mistakes to Avoid
Mistake 1: Assuming JmsClient Can Receive
// WRONG: JmsClient doesn't have receive methodsOrder order = jmsClient.receive("order.queue"); // This won't compile!Use JmsTemplate instead:
// CORRECT: Use JmsTemplate for receivingOrder order = (Order) jmsTemplate.receiveAndConvert("order.queue");Mistake 2: Migrating Receive Operations
If you’re migrating to JmsClient, don’t forget that your receive operations still need JmsTemplate:
@Servicepublic class OrderService {
@Autowired private JmsClient jmsClient; // For sending
@Autowired private JmsTemplate jmsTemplate; // For receiving
public void sendOrder(Order order) { jmsClient.send(order) .to("order.queue") .execute(); }
public Order receiveOrder() { return (Order) jmsTemplate.receiveAndConvert("order.queue"); }}Mistake 3: Not Updating Tests
When you switch to JmsClient, make sure to update your tests:
@SpringBootTestclass OrderServiceTest {
@Autowired private JmsClient jmsClient;
@Test void shouldSendOrder() { Order order = new Order("123", "Laptop");
jmsClient.send(order) .to("test.queue") .execute();
// Verify the message was sent }}Migration Strategy
If you decide to migrate from JmsTemplate to JmsClient for sending operations, here’s my recommended approach:
- Identify send-only code: Find all places where you only send messages (no receive operations)
- Add JmsClient dependency: Spring Boot 3.x+ with Spring Framework 7.0 includes JmsClient
- Migrate incrementally: Start with new code or simple send operations
- Keep JmsTemplate for receive: You’ll still need it for consuming messages
- Update tests: Ensure your tests cover the new API
Summary
In this post, I compared JmsClient and JmsTemplate for JMS messaging in Spring 7.0. The key takeaway is:
- Use
JmsClientfor new projects that prioritize clean, chainable syntax and only need to send messages - Keep
JmsTemplatefor advanced features like receive operations, browse operations, or when working with legacy Spring applications
Both APIs can coexist in the same application, so you don’t have to choose one exclusively. Use each for what it does best.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments