9 Common Reasons Why @Async Doesn’t Work in Spring and How to Fix Them

This article explains nine typical scenarios that cause the Spring @Async annotation to fail—ranging from missing @EnableAsync to improper method visibility, return types, static or final modifiers, missing @Service, manual object creation, and incorrect component scanning—providing clear explanations and code examples for each case.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
9 Common Reasons Why @Async Doesn’t Work in Spring and How to Fix Them

Introduction

A fan asked why a method annotated with @Async still executes synchronously, rendering the asynchronous behavior ineffective. Below is a pseudocode example:

@Slf4j
@Service
public class UserService {

    @Async
    public void async(String value) {
        log.info("async:" + value);
    }
}

The article summarizes nine situations where @Async fails.

1. Missing @EnableAsync Annotation

To enable @Async, the project’s main class or a configuration class must be annotated with @EnableAsync. Without it, the asynchronous feature remains disabled.

@EnableAsync
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

2. Internal Method Calls

Calling an @Async method from another method within the same class uses the original object (equivalent to this.async()) instead of the Spring‑generated proxy, causing the async behavior to be lost.

@Slf4j
@Service
public class UserService {

    public void test() {
        async("test");
    }

    @Async
    public void async(String value) {
        log.info("async:{}", value);
    }
}

The method must be invoked on the proxy bean for @Async to work.

3. Method Not Public

Spring AOP can only proxy public methods. If an @Async method is declared with private, protected, or package‑private visibility, the asynchronous feature will not be applied.

4. Incorrect Return Type

@Async methods must return void or a Future type. Returning other types (e.g., String) prevents the async execution.

@Service
public class UserService {

    @Async
    public String async(String value) {
        log.info("async:{}", value);
        return value;
    }
}

5. Method Declared static

Static methods cannot be overridden, so Spring cannot create a proxy for them. Annotating a static method with @Async results in a compilation error: “Methods annotated with '@Async' must be overridable.”

@Slf4j
@Service
public class UserService {

    @Async
    public static void async(String value) {
        log.info("async:{}", value);
    }
}

6. Method Declared final

Final methods are also non‑overridable, leading to the same proxy‑generation issue as static methods.

@Slf4j
@Service
public class UserService {

    public void test() {
        async("test");
    }

    @Async
    public final void async(String value) {
        log.info("async:{}", value);
    }
}

7. Service Class Missing @Service (or @Component/@Controller)

If a class containing an @Async method is not managed by Spring (i.e., lacks @Service, @Component, or @Controller), the async functionality will not be applied.

@Slf4j
// @Service   // missing annotation
public class UserService {

    @Async
    public void async(String value) {
        log.info("async:{}", value);
    }
}

@Service
public class TestService {

    @Autowired
    private UserService userService;

    public void test() {
        userService.async("test");
    }
}

8. Manually Instantiating the Service

Creating a service instance with new bypasses Spring’s container, so the proxy (and thus @Async) is never applied.

@Slf4j
@Service
public class UserService {

    @Async
    public void async(String value) {
        log.info("async:{}", value);
    }
}

@Service
public class TestService {

    public void test() {
        UserService userService = new UserService();
        userService.async("test");
    }
}

To use async behavior, obtain the bean from the Spring context or inject it.

9. Component Scan Does Not Include the Async Class

If @ComponentScan points to a non‑existent package or omits the package containing the @Async service, Spring will not detect the bean, and the async feature will be disabled.

@ComponentScan({"com.susan.demo.service1"})
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Ensure that the scan path includes all packages with async‑enabled services.

Conclusion

These nine scenarios cover the most common pitfalls that cause @Async to be ineffective. By checking each condition—enabling @EnableAsync, using public non‑static non‑final methods, returning appropriate types, annotating services, letting Spring manage beans, and configuring component scanning—you can ensure proper asynchronous execution in Spring applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaaopspringSpring BootAsyncAsynccommon pitfalls
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.