Class FlowContextTaskDecorator

java.lang.Object
com.fayupable.logged.spring.aspect.FlowContextTaskDecorator
All Implemented Interfaces:
org.springframework.core.task.TaskDecorator

public final class FlowContextTaskDecorator extends Object implements org.springframework.core.task.TaskDecorator
TaskDecorator that carries the submitting thread's FlowContext and MDC context into whatever thread Spring's @Async infrastructure actually runs the task on.

Unlike FlowContextPropagatingExecutor, which a consuming application wraps around an Executor it manages directly, @Async methods are dispatched by Spring's own TaskExecutor machinery, which the calling code never sees or controls directly. TaskDecorator is the extension point Spring itself provides for exactly this situation: it decorates every Runnable submitted to a TaskExecutorAdapter or ThreadPoolTaskExecutor before it is dispatched, regardless of which underlying thread pool executes it.

This class deliberately does not wire itself into every TaskExecutor bean automatically. Auto-attaching to any executor found in the application context would decorate executors the consuming application did not intend for this library to touch, which is a surprising, hard-to-diagnose side effect. Instead, a consuming application registers this decorator explicitly on the specific executor it wants @Logged call chains to survive across:


 @Bean
 public TaskExecutor taskExecutor() {
     ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
     executor.setTaskDecorator(new FlowContextTaskDecorator());
     executor.initialize();
     return executor;
 }
 

Without this registration, a @Logged method invoked through @Async starts a brand new call chain instead of continuing the caller's: the trace id changes and the depth resets to zero, breaking the ability to reconstruct the full call chain from log output.

  • Constructor Details

    • FlowContextTaskDecorator

      public FlowContextTaskDecorator()
  • Method Details

    • decorate

      public Runnable decorate(@NonNull Runnable runnable)
      Captures the calling thread's current FlowContext and MDC context map, and returns a wrapped task that restores both before running runnable and restores the executor thread's own previous state again afterward, regardless of whether runnable completes normally or throws.

      The MDC context map is captured and restored in full, not limited to LoggedMdcKeys: see MdcContextPropagation for why.

      Specified by:
      decorate in interface org.springframework.core.task.TaskDecorator
      Parameters:
      runnable - the task Spring's @Async infrastructure is about to hand off to an executor thread
      Returns:
      a task carrying the calling thread's flow context and MDC