Spring
Async
Autowired
Dependency Injection
Java

Spring Async not allowing use of autowired beans

Object-Oriented Design practice on Codemia

Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.

Practice OOD

Introduction

When a Spring @Async method seems unable to use an autowired bean, the problem is usually not dependency injection itself. In most cases, the real issue is that the async method is not running through a Spring proxy, the class was created manually with new, or async support was never enabled. Once the method is invoked on a real Spring-managed bean, autowired dependencies work normally.

@Async Works Only on Spring-Managed Beans

Spring implements @Async through proxies. That means the method has to be called on a bean created by the container, not on an object you instantiated yourself.

Correct setup:

java
1import java.util.concurrent.CompletableFuture;
2import org.springframework.scheduling.annotation.Async;
3import org.springframework.stereotype.Service;
4
5@Service
6public class ReportService {
7
8    private final MailService mailService;
9
10    public ReportService(MailService mailService) {
11        this.mailService = mailService;
12    }
13
14    @Async
15    public CompletableFuture<Void> sendReportAsync(String email) {
16        mailService.send(email, "Report is ready");
17        return CompletableFuture.completedFuture(null);
18    }
19}

If ReportService is a real Spring bean, mailService is injected before the async method runs. There is nothing special about autowiring here.

Enable Async Support Explicitly

The next common issue is forgetting @EnableAsync. Without it, Spring never creates the async proxy layer.

java
1import org.springframework.context.annotation.Configuration;
2import org.springframework.scheduling.annotation.EnableAsync;
3
4@Configuration
5@EnableAsync
6public class AsyncConfig {
7}

If this annotation is missing, the method may still run, but it runs synchronously and none of the async behavior is applied.

Avoid Self-Invocation

Another frequent trap is calling the @Async method from another method in the same class. That bypasses the Spring proxy, so the async interceptor never gets a chance to run.

Problematic pattern:

java
1import org.springframework.scheduling.annotation.Async;
2import org.springframework.stereotype.Service;
3
4@Service
5public class JobService {
6
7    public void startJob() {
8        runAsyncTask();
9    }
10
11    @Async
12    public void runAsyncTask() {
13        System.out.println("Running in async?");
14    }
15}

startJob() calls runAsyncTask() directly on this, not through the proxy. Move the async method into another bean and inject that bean instead.

java
1import org.springframework.stereotype.Service;
2
3@Service
4public class JobStarter {
5
6    private final AsyncJobExecutor asyncJobExecutor;
7
8    public JobStarter(AsyncJobExecutor asyncJobExecutor) {
9        this.asyncJobExecutor = asyncJobExecutor;
10    }
11
12    public void startJob() {
13        asyncJobExecutor.runAsyncTask();
14    }
15}

This is one of the most important fixes when people think @Autowired "stops working" in async code.

Do Not Instantiate Beans Manually

If you write new ReportService() anywhere, Spring cannot inject dependencies into that instance. The autowired field or constructor dependency will be missing because the object never passed through the container.

Wrong:

java
ReportService reportService = new ReportService();
reportService.sendReportAsync("[email protected]");

Right:

java
1import org.springframework.stereotype.Component;
2
3@Component
4public class ReportController {
5
6    private final ReportService reportService;
7
8    public ReportController(ReportService reportService) {
9        this.reportService = reportService;
10    }
11}

Once the bean comes from Spring, autowiring and async proxies can both do their jobs.

Think About Thread Safety

Autowired beans are usually singleton beans. That is fine in async code as long as they are stateless or properly synchronized. The async method runs on another thread, so shared mutable state still needs the same care it would need anywhere else.

This matters most for:

  • mutable in-memory caches
  • request-scoped data accessed outside the request thread
  • non-thread-safe helpers stored as singletons

The injection is not the danger. Unsafe shared state is.

Return a Useful Async Type

For fire-and-forget operations, void can work, but CompletableFuture is often better because it gives the caller a way to observe completion or failure.

java
1@Async
2public CompletableFuture<String> computeAsync() {
3    return CompletableFuture.completedFuture("done");
4}

This makes testing and error handling much clearer than hiding all outcomes in background threads.

Common Pitfalls

  • Forgetting @EnableAsync, so the async proxy is never created.
  • Calling an @Async method from another method in the same class.
  • Instantiating the service with new instead of letting Spring create it.
  • Assuming autowired singleton beans are unsafe by default, when the real problem is shared mutable state.
  • Returning void everywhere and making async errors harder to observe.

Summary

  • '@Autowired beans work in @Async methods as long as the service is a real Spring-managed bean.'
  • '@EnableAsync must be present for async proxies to exist.'
  • Self-invocation bypasses the proxy and is a common reason async behavior appears broken.
  • Never create Spring services manually with new.
  • Treat thread safety as a separate concern from dependency injection.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Object-Oriented Design practice on Codemia

Turn requirements into classes, and defend the design, on the problems that come up in OOD rounds.

Practice OOD

All Rights Reserved.