-
Notifications
You must be signed in to change notification settings - Fork 38.3k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Enforce Future or void return declaration for each asynchronously executed method (e.g. with class-level @Async) #27734
Comments
The documentation states the limitations in the return types only So I the return type isn't a As a solution an exception would be better imho with a clear message stating that only |
As the documentation states and as @mdeinum pointed out, the return type must I think this can be closed, unless @jhoeller you see some opportunity to bypass methods that don't return void or Future. |
I'm inclined to explicitly throw an exception for non-Future/void return type declarations whenever we attempt to execute a method asynchronously. While this may not be much of an issue with an explicit annotated method, a class-level |
Noob question @jhoeller since I assume you systematically do an instanceof/reflection check, and that could incur a slowdown, wouldn't it be better to have this check only enabled on dev/debug mode et disabled on release mode? |
@LifeIsStrange as you can see in the code, what is done is a As for your question about the
Nevertheless, it doesn't apply to this fix. |
spring-framework/spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionInterceptor.java
Lines 113 to 127 in 79d3f5c
I have found an odd behaviour working with
@Async
-annotated classes in Spring. Please note that there is a fundamental error in my code. Unfortunately, this post has to be long and detailed.Let's say I have already made a synchronous REST API generated by Swagger generator. Following code omits all documentation-level annotations
This API is easily implemented via
RestTemplate
, but I won't discuss the inner details.Now, suppose I want to provide an async version to developers consuming the API. What I have done is to create another interface with some search&replace-fu 🥋🥋
With the search&replace, I basically created an async-ish version of every method that should be backed by Spring's
@Async
annotation. My original idea was that synchronous methods can be invoked as they are, but if you instantiateTaxonomiesApiAsync
you also have access to the async version.I have discovered I made a fundamental mistake by applying the
@Async
annotation at interface level when the class contains both sync and async methods. I found that synchronousdisableItem
was performed in the same@Async
context. Accoding to design (correctly), Spring found the@Async
annotation at interface level so every method, including inherited ones, was invoked asynchronously.But the method always returned null. By debugging and looking at the code, I found that Spring tries to resolve the return value of the invoked method only if it's a
Future
. What if the returned value is a Present object?That means that if the returned value is not a
Future<ResponseEntity<GenericTaxonomyItem>>
but rather just aResponseEntity<GenericTaxonomyItem>
Spring neither throws an exception nor returns that value directly.Example of working calling code (invoking a different method)
Example of non-working code; the result of the CompletableFuture is always null.
In this code, I decided not to use the executor embedded in the API service, but rather the executor injected in the consuming service. So I ran a sync method in an executor, expecting the same result.
Since I spent one hour debugging that problem, I decided to spend more of my after-work time to document the issue here.
Proposed fix
In the code I linked, if the
instanceof
check fails the returned value is simply null. I don't yet understand the implications, but what about not unwrapping the value from Future if that's not a future? I meanreturn result
The text was updated successfully, but these errors were encountered: