Messaging without blind spots
Understand listener additions, consumer retirement, routing changes and the difference between direct saves and dependency cascades.
Start with configured messaging infrastructure
Reclazz can add supported listener methods to already-running plain singleton components. It uses your application’s native listener processors, factories and conversion. Compile the listener change and inspect both registration and delivery.
Use explicit stable listener IDs. Original consumers are retired before a replacement bean is created, and unrelated registry entries are preserved. Proxies, custom adapters, mixed broker ownership and unsupported factory shapes can require restart. A class-body reload may already have happened when listener follow-up fails.
Kafka: topics, groups and native dispatch
@KafkaListener(id = "orders-created",
topics = "orders", groupId = "orders-service")
public void onOrder(String payload) {
processOrder(payload);
}Added public/private void instance callbacks support native payload/header conversion. Topics and groups accept their documented literal, placeholder and expression forms; __listener resolves against the real singleton. Literal factory, concurrency, idIsGroup and autoStartup options have bounded support.
Re-save the listener to reevaluate its route. Changing only a routing bean or property is not a general reroute trigger. An expression error after retirement can leave the new registration absent; save a valid listener to retry. Changing the consumer group can replay retained records.
Use the standard record-mode factory and registry. Batch callbacks, retry/filter/customized factories, arbitrary advice and repeatable/class-level additions are excluded. Stop callbacks must finish within the 30-second wait; otherwise follow-up is deferred and ownership retained.
Named direct lanes: Kafka 2.9.13 / Framework 5.3.39 / embedded Kafka 3.2.3, and Kafka 3.2.4 / Framework 6.1.14 / embedded Kafka 3.7.1, on stock JDK 17/21.
JMS: destinations and synchronous replies
@JmsListener(id = "order-status", destination = "order-status-requests")
@SendTo("${order.status.replies}")
public String status(String orderId) {
return findOrderStatus(orderId);
}Added callbacks support the documented void/text/bytes/map/native-message/Spring-message results. A non-null reply uses the request’s JMSReplyTo ahead of direct method @SendTo; null sends nothing. Spring retains correlation and conversion.
Destination and selector placeholders are supported; SpEL destinations, XA, durable/shared topics, custom factories/executors and proxy receivers are outside this addition. Native message types differ between javax and Jakarta lanes. Do not generalize the allowed result types to arbitrary DTO, Future or reactive returns.
Named lanes: Framework/JMS 5.3.39 with ActiveMQ Classic 5.19.11, and Framework/JMS 6.1.14 with Jakarta Messaging 3.1.0 / ActiveMQ Classic 6.1.3, on stock JDK 17/21. Modern native-message and String reply observers are recorded separately.
RabbitMQ: wait for the actual workers
@RabbitListener(id = "orders-created", queues = "orders")
public void onOrder(String payload) {
processOrder(payload);
}Added public/private void callbacks support literal, property and documented SpEL queue values, including names and native Queue objects resolved by Spring. Existing original listeners may retain generated IDs; newly added callbacks require an explicit nonempty literal ID.
Use standard simple containers with AUTO acknowledgment and the default SimpleAsyncTaskExecutor. Newly added consumers require the standard simple factory without a customizer. Local channel transactions are supported. Direct/stream/batch containers, custom executors, external transactions, inline binding declarations, method advice and added return/reply/async callbacks are excluded.
The startup hook tracks real workers through complete exit. A zero consumer count alone is not sufficient. Retirement waits up to 30 seconds for destruction and worker completion; timeout/interruption preserves registry ownership and defers recreation. A later direct class save waits for the same retirement.
Named direct lanes: Spring Rabbit 2.4.17 / Framework 5.3.39 and Rabbit 3.1.7 / Framework 6.1.14, RabbitMQ 4.3.5, stock JDK 17/21 on macOS arm64.
A helper or property save is a separate feature
JMS, Kafka and Rabbit each have a separately measured transaction for reconstructing a plain local leaf listener after a helper-body or scalar helper-constructor property edit. Each transaction has its own root-context, factory, owner and callback restrictions in the constructor dependency guide.
The Rabbit indirect path is measured on Framework 5.3.39/AMQP 2.4.17 and Framework 6.1.14/AMQP 3.1.7, stock JDK 17/21 on macOS arm64. It requires one plain local helper/leaf owner, one void callback with a literal ID, standard native simple containers, AUTO acknowledgment and nontransacted channels. Direct listener reload support for local channel transactions does not extend to this helper/property path. Complete worker retirement precedes reconstruction; a stop timeout or partial reconstruction requires restart.
These bounded observations do not establish arbitrary indirect listener graph refresh, distributed delivery or exactly-once behavior.
Verify the consumer, not just the class
- Record the original listener ID, route and a uniquely identifiable test message.
- Compile the change and wait for framework follow-up.
- Send a new test message and check the expected handler result.
- For a route change, check both the new route and retirement of the old consumer.
- Inspect diagnostics before retrying a partial registration or retirement timeout.
Use disposable development data: a valid retry can redeliver messages, and saving old source does not undo application side effects.
Sources & version scope
Documentation source 1.4.0 · evidence baseline 75535bd · reviewed 19 October 2026. Check your installed artifact; source features may postdate a release.