2

Consider the following scenario: A class called EventHandlerImpl implements an interface called AsyncEventHandler, with a single handle() method marked with the Spring @Async annotation. EventHandlerImpl also extends an abstract superclass called AbstractEventHandler. This abstract superclass implements the plain EventHandler interface which also contains a handle() method, providing an implementation of the method which in turn calls upon another abstract method designed to be implemented by child classes.

public interface EventHandler {
    void handle();
}
public interface AsyncEventHandler {
    @Async
    void handle();
}
@Component
public class EventHandlerImpl extends AbstractEventHandler implements AsyncEventHandler {
    @Override
    public void wrappedMethod() {
        longRunningMethodWhichMayThrowException();
        System.out.println("Handled long-running task!");
    }
}
public class AbstractEventHandler implements EventHandler {
    protected abstract void wrappedMethod();

    @Override
    public void handle() {
        try {
            wrappedMethod();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

Then, the required bean is injected in a constructor:

@Autowired
public DependentComponent(AsyncEventHandler asyncEventHandler) {
    this.asyncEventHandler = asyncEventHandler;
}

The expected behaviour for the injected bean is asynchronous execution of its handle method as per the AsyncEventHandler interface (this is the entire reason for the existence of this interface).

Example:

System.out.println("Before long-running task execution");
asyncEventHandler.handle();
System.out.println("After long-running task execution");

Expected output:

Before long-running task execution
After long-running task execution
Handled long-running task!

The behaviour seen in reality does not match this. The handle() method is not executed asynchronously by Spring. I've found that having the AsyncEventHandler interface declared as such:

@Async
public interface AsyncEventHandler {
    void handle();
}

(annotation on the interface level rather than the method level) makes the bean behave as expected. Digging into the Spring source code responsible for attaching and executing advisors such as the async advisor confirms that Spring is able to find the @Async annotation on the interface, but is unable to do so when it exists on the interface method in this particular scenario.

This raises the question: How is Spring meant to handle searching for annotations when the "same" method signature exists in multiple places in the inheritance hierarchy (with potentially different annotations)?

Is the discrepancy between annotating the method vs the interface a defined behaviour for Spring?

vsilin
  • 51
  • 3
  • Does this answer your question? [Are annotations applicable to subclasses?](https://stackoverflow.com/questions/33403417/are-annotations-applicable-to-subclasses) – BadZen Jun 24 '21 at 14:32
  • Doesn't appear so, unfortunately. The answer there doesn't appear to address the discrepancy between finding annotations present on supertypes vs supertype methods. – vsilin Jun 24 '21 at 14:52

0 Answers0