How not to use CompletableFuture as a shared state with EventListener in Spring

How not to use CompletableFuture as a shared state with EventListener in Spring

Problem Description:

I need to asynchronously react to the @EventListener, therefore I’ve created something like this.

@Service
public class AsyncHandler {

    private CompletableFuture<My> future;

    @Async
    public CompletableFuture<My> getMy() {
        future = new CompletableFuture<>();
        return future;
    }

    @EventListener
    public void processEvent(MyEvent event) {
        future.complete(event.my());
    }
}

The problem here is that AsyncHandler is now stateful. Which is wrong.
And I don’t want to use database, so is there any other way to make the bean stateless while using @EventListener?

Solution – 1

You are right, your singleton has state, which is "not good".

One (possible) solution:

  1. make the/refactor "statfeul part" to a "prototype" (request, session) scope bean.
  2. make your "singleton" abstract!
  3. inject the "stateful part" via "method injection" (we cannot "auto wire" lower(shorter-living) scope beans to higher ones…)

As code (example):

  1. State holder:
    public class MyStateHolder {
       // State:
       private CompletableFuture<My> future;
    
       @Async // ?
       public CompletableFuture<My> getMy() {
         future = new CompletableFuture<>();
         return future;
       }
    }
    
  2. Abstract, (no @Service ..yet, no state!):
    public abstract class AsyncHandler {
      @EventListener
      public void processEvent(MyEvent event) { 
         // !!
         delegate().getMy().complete(event.my());
      }
    
      // and now only (abstract!):
      public abstract MyStateHolder delegate();
    }
    
  3. Wiring :
    @Configuration
    class MyConfig {
    
      @Bean
      @Scope("prototype") // !
      public MyStateHolder stateful() {
        return new MyStateHolder();
      }
    
      @Bean // singleton/service:
      public AsyncHandler asyncHandler() {
        return new AsyncHandler() { // !
          @Override // !
          public MyStateHolder delegate() {
            return stateful();// !;)
          }
        };
      }
    }
    

refs: (most of) https://docs.spring.io/spring-framework/docs/current/reference/html/core.html

Especially:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-scopes-sing-prot-interaction

Rate this post
We use cookies in order to give you the best possible experience on our website. By continuing to use this site, you agree to our use of cookies.
Accept
Reject