I thought I understood how Spring deals with prototype-scope beans: when injected into another bean as a dependency, or explicitly retrieved from a
getBean, a new instance is always created. Thus, when injected as a dependency the prototype bean effectively shares the same lifecycle as the bean it’s injected into (except that destroy listeners aren’t called).
That’s correct if scoped proxies aren’t being used, but is not correct if they are. If scoped proxies are enabled, a scoped proxy is instead injected, and a new instance of the prototype bean is created every time the proxy is accessed! I don’t see that behavior discussed in the Spring docs. It seems like it fulfils the goals of method injection in an easier fashion.
While that behavior might be useful at times, it seems a rather subtle way to enable it. If explicitly declaring the
proxyMode when declaring bean scope, it’s obvious enough, but if declaring the type of scoped proxies you want across the system using
scopedProxy on a
@ComponentScan annotation (or
scoped-proxy on a
<context:component-scan> tag), this drastically changes the behavior of all prototype-scope beans it picks up.
Perhaps the best practice is to always explicitly declare a
proxyMode attribute on all
@Scope("prototype") annotations, to make the decision explicit.
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.