Why is this an issue?

A method annotated with Spring’s @Async or @Transactional annotations will not work as expected if invoked directly from within its class.

This is because Spring generates a proxy class with wrapper code to manage the method’s asynchronicity (@Async) or to handle the transaction (@Transactional). However, when called using this, the proxy instance is bypassed, and the method is invoked directly without the required wrapper code.

How to fix it

Replace calls to @Async or @Transactional methods via this with calls on an instance that was injected by Spring (@Autowired, @Resource or @Inject). The injected instance is a proxy on which the methods can be invoked safely.

Code examples

Noncompliant code example

@Service
public class AsyncNotificationProcessor implements NotificationProcessor {

  @Override
  public void process(Notification notification) {
    processAsync(notification); // Noncompliant, call bypasses proxy
  }

  @Async
  public processAsync(Notification notification) {
    // ...
  }
}

Compliant solution

@Service
public class AsyncNotificationProcessor implements NotificationProcessor {

  @Resource
  private AsyncNotificationProcessor

  @Override
  public void process(Notification notification) {
    asyncNotificationProcessor.processAsync(notification); // Compliant, call via injected proxy
  }

  @Async
  public processAsync(Notification notification) {
    // ...
  }
}

Resources

Documentation

Articles & blog posts