A DirectChannel invokes its handler on the sender's thread; an ExecutorChannel can dispatch on another thread and lose that transaction context.
Spring Integration DirectChannel versus ExecutorChannel: where the transaction stops
Locate the handoff
A parcel intake service writes a row and sends a message to an allocation handler. With DirectChannel, the handler runs in the sender's call path, so a failure can propagate to the caller and its transaction. With ExecutorChannel, the send can return before the handler runs; the handler may use a different thread and a separate transaction. A saturated executor may even use the caller thread under a caller-runs policy, so that accidental behavior cannot be the transaction plan.
Give asynchronous work its own contract
If allocation must survive a process exit, use durable storage or an outbox rather than relying on a successful send to an in-memory executor. If the work is disposable, specify the queue limit, rejection behavior and completion observation. Imperative transaction state does not cross a thread handoff simply because a Message carries the same business ID.
Test both outcomes
In one integration test, make the handler throw and assert that DirectChannel send fails before the caller commits. In another, block an ExecutorChannel handler, let send return, and verify that a later handler failure does not roll back the already completed caller. This code is an implementation sketch; wire it to the actual executor and transaction manager before claiming those outcomes.
Implementation sketch
@Bean
DirectChannel inlineAllocation() {
return new DirectChannel();
}
@Bean
ExecutorChannel deferredAllocation(TaskExecutor allocationExecutor) {
return new ExecutorChannel(allocationExecutor);
}Cost and verification
Direct dispatch consumes the caller thread until the handler finishes. Executor dispatch adds scheduling and queue pressure; it does not supply durability or a shared transaction.
Common Mistakes
- Do not equate a returned ExecutorChannel send with completed business work.
- Do not rely on caller-runs behavior to preserve transaction context.
- Do not insert an asynchronous channel between two writes that must commit atomically.
Read next
Spring Integration queue: bound memory and choose a real message store, Spring imperative transaction: a worker thread does not inherit it, Spring async work has two failure points: submission and completion, Spring outbox transaction boundaries: where atomicity ends.
