Async not working in Spring API rest with Interfaces
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
When @Async appears to run synchronously in a Spring REST application, the cause is usually proxy behavior, not thread scheduling bugs. This is especially confusing when interfaces are involved, because method interception rules depend on how beans are proxied and invoked. A reliable fix starts with understanding exactly how Spring applies @Async.
How Spring @Async Actually Works
@Async is implemented through proxy interception. A call must go through a Spring managed proxy for asynchronous execution to occur.
That means three prerequisites:
- async support is enabled with
@EnableAsync, - the target method is called through an injected bean reference,
- the proxy can intercept the method.
If any one of those is missing, the call runs on the caller thread.
Correct Baseline Configuration
Start with explicit async configuration and an executor bean.
Service interface and implementation:
Controller calling through injected interface:
Why It Fails In Real Projects
Self invocation
If a method in ReportServiceImpl calls another @Async method in the same class, that call bypasses proxy interception. It executes synchronously.
Method visibility and proxy mode
Proxy interception works best for public methods. Private methods or final methods can block proxy behavior depending on proxy strategy.
Missing @EnableAsync
Without global async enablement, @Async annotations are ignored.
Wrong return expectations
If you return a plain value type and then immediately wait for it in the controller, you can accidentally remove async benefits.
Patterns To Avoid Self Invocation Issues
Split orchestration and async work into separate beans.
- Bean A receives HTTP request.
- Bean A calls Bean B async method through injected dependency.
- Bean B owns background logic.
This guarantees call path goes through proxy and keeps responsibilities clear.
Error Handling And Observability
Unhandled exceptions in async methods can be easy to miss. For CompletableFuture, attach error handling stages. For void async methods, configure AsyncUncaughtExceptionHandler.
Also log thread names in test and dev environments. If all logs show request thread names, proxy interception is still not happening.
Testing The Behavior
A quick integration test should assert that returned thread name starts with your executor prefix. That confirms the method did not execute on the caller thread.
You can also assert that controller response returns before a long async operation completes, depending on API design.
Common Pitfalls
- Calling
@Asyncmethods from within the same bean instance. - Forgetting
@EnableAsyncin configuration. - Not declaring a dedicated executor and relying on defaults without understanding limits.
- Using non public methods for async work and expecting interception.
- Blocking immediately on async result and losing non blocking benefit.
Summary
- Spring
@Asyncdepends on proxy interception, not magic thread switching. - Calls must pass through an injected Spring bean reference.
- Interfaces are fine, but self invocation still bypasses proxies.
- Use explicit executor configuration and thread name verification.
- Split beans and add tests to ensure async behavior is truly active.

