SoFunction
Updated on 2025-04-07

Solution to SpringBoot asynchronously call the same class

In previous articles, we learned about the asynchronous calling mechanism in Spring Boot and how to use @Async annotation to implement asynchronous execution of methods. However, in actual development, we may encounter a problem: when calling a method with @Async annotation in the same class, the asynchronous call actually fails! Why is this? Today, we will explore this problem in depth and provide several feasible solutions.

1. Problem introduction

In Spring Boot, @Async annotations are widely used to implement asynchronous execution of methods. Generally, we only need to add the @Async annotation to the method and the @EnableAsync annotation to the startup class to implement asynchronous calls of the method. However, when we call a method with @Async annotation in the same class, we find that the asynchronous call does not take effect and the method is still executed synchronously.

Cause analysis

The root of this problem lies in Spring's AOP (System-Oriented Programming) proxy mechanism. In Spring, the implementation of @Async annotation relies on the AOP proxy. When we call another method in one class, if we call this directly (i.e., call it internally), then the method is not actually called through Spring's proxy object, so the @Async annotation cannot take effect.

2. Solution

2.1 Split the asynchronous method into another bean

This is the easiest and most recommended solution. We can split the asynchronous method with @Async annotation into another bean, then inject this bean into the original class, and implement asynchronous execution by calling the method of this bean.

step:

The bean where the asynchronous method is created‌:

@Service
public class AsyncService {
    @Async
    public void asyncMethod() {
        // Implementation of asynchronous methods        ("Async method starts");
        try { (1000); } catch (InterruptedException e) {}
        ("Async method ends");
    }
}

Inject asynchronous bean into the original class and call it‌:

@Service
public class MyService {
    @Autowired
    private AsyncService asyncService;
    public void myMethod() {
        ("My method starts");
        ();
        ("My method ends");
    }
}

Realize the effect:

  • When calledmyMethodhour,asyncMethodWill execute asynchronously in a separate thread.
  • Console output order:My method starts -> My method ends -> Async method starts -> Async method ends(Note that the order here may vary due to thread scheduling).

2.2 Self-injection of the current bean

If for some reason we don't want to split the asynchronous method into another bean, then we can consider using a self-injection method. That is, inject yourself into the original class, and then call the asynchronous method through the injected instance.

Steps:Inject yourself into the original class‌:

@Service
public class MyService {
    @Autowired
    private MyService self;
    public void myMethod() {
        ("My method starts");
        ();
        ("My method ends");
    }
    @Async
    public void asyncMethod() {
        // Implementation of asynchronous methods        ("Async method starts");
        try { (1000); } catch (InterruptedException e) {}
        ("Async method ends");
    }
}

Notice‌: Using self-injection requires ensuring that there are no circular dependencies. Usually, if it's just a method call, there may not be any problem. But to avoid potential risks, we can use the @Lazy annotation to delay the loading of the bean.

Realize the effect:

  • When calledmyMethodhour,asyncMethodWill execute asynchronously in a separate thread.
  • The console output order is similar to the previous solution.

2.3 Get proxy object using AopContext

Another solution is to use AopContext to get the proxy object of the current class and call the asynchronous method through the proxy object. But this method requires the exposeProxy option to be enabled, and the code may not be intuitive enough.

step:

Turn on exposeProxy on the startup class‌:

@SpringBootApplication
@EnableAsync
@EnableAspectJAutoProxy(exposeProxy = true)
public class MyApplication {
    public static void main(String[] args) {
        (, args);
    }
}

Get proxy object using AopContext in method‌:

@Service
public class MyService {
    public void myMethod() {
        ("My method starts");
        ((MyService) ()).asyncMethod();
        ("My method ends");
    }
    @Async
    public void asyncMethod() {
        // Implementation of asynchronous methods        ("Async method starts");
        try { (1000); } catch (InterruptedException e) {}
        ("Async method ends");
    }
}

Realize the effect:

  • When calledmyMethodhour,asyncMethodWill execute asynchronously in a separate thread.
  • The console output order is similar to the previous two solutions.

Notice‌: The method of using AopContext to get proxy objects needs to ensure that the AOP proxy has been exposed correctly and that there are type conversions in the code, which may not be safe enough. Therefore, it needs to be used with caution in actual development.

2.4 Get Bean instance using ApplicationContext

Another way is to use ApplicationContext to get the Bean instance and call the asynchronous method through that instance. But this approach may have circular dependencies or the code does not look intuitive enough.

Steps:Inject ApplicationContext in class‌:

@Service
public class MyService {
    @Autowired
    private ApplicationContext applicationContext;
    public void myMethod() {
        ("My method starts");
        MyService myService = ();
        ();
        ("My method ends");
    }
    @Async
    public void asyncMethod() {
        // Implementation of asynchronous methods        ("Async method starts");
        try { (1000); } catch (InterruptedException e) {}
        ("Async method ends");
    }
}

Realize the effect:

  • When calledmyMethodhour,asyncMethodWill execute asynchronously in a separate thread.
  • The console output sequence is similar to the previous solutions.

Notice‌: Methods that use ApplicationContext to get Bean instances may have problems with circular dependencies, especially in complex dependencies. Therefore, it is necessary to use it carefully in actual development and ensure that no new problems are introduced.

Summarize

  • Recommended plan: Split the asynchronous method into another bean, the code structure is clear and conforms to Spring design.
  • Self-injection: Suitable for simple scenarios, pay attention to circular dependencies.
  • AopContext: Flexible but requires additional configuration, suitable for situations where class structure cannot be modified.

The above is the detailed content of SpringBoot asynchronously calling methods of the same class. For more information about SpringBoot asynchronous calling, please follow my other related articles!

This is the end of this article about the solution to the same class of SpringBoot asynchronous calls. For more related SpringBoot asynchronous calls, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!