The Spring Framework for Java supports an
@Async annotation which specifies that the annotated method should be run asynchronously in a background thread. (See here for details.)
The docs did not [obviously] point out some important notes around usage of this annotation with interface-based proxying. After spending some time stepping through Spring code, I discovered the following:
When Using Interface-Based Proxying, Classes with
@Async Methods Require Interfaces
Support for the
@Async annotation is implemented using a Spring dynamic proxy. When Spring is configured to use interface-based proxies (as opposed to CGLib proxies), in order for a bean to have dynamic proxy:
- it must implement an interface, and
- you must inject the bean into dependent classes referencing the interface rather than the concrete class.
The same rules apply when using scoped beans. However, if you don’t fulfill the requirements in that case, you get an error at startup. With
@Async-tagged methods, I don’t see any warning in the log, and it will simply not run the method asynchronously.
@Async Annotations Specifying an Executor Must Be On the Interface
If you specify an explicit executor to use in the value of the
@Async annotation (e.g.,
@Async("myCustomExecutor")), the annotation must be on the interface method definition, not on the concrete method implementation.
If you put the annotation on the concrete method implementation, it will be run asynchronously. However, it will always use the default executor. This is because the code to determine the executor to use (in
org.springframework.aop.interceptor.AsyncExecutionAspectSupport.determineAsyncExecutor(Method)) is looking for annotations on the method and class being referenced by the caller, which is always going to be the interface method due to the rules in the previous section.