assertThat(isParameterizedTypeAssignable.invoke(null, getGenericReturnTypeByName("m1"),
getGenericReturnTypeByName("m2"))).isEqualTo(Boolean.TRUE);
assertThat(isParameterizedTypeAssignable.invoke(null, getGenericReturnTypeByName("m2"),
getGenericReturnTypeByName("m2_1"))).isEqualTo(Boolean.FALSE);
class MyClass {
<T> List<T> m1() {
return null;
<T> List<T> m2() {
return null;
T
s are really different because they are local to the methods they are declared on.
It is no difference with any local variables even if they have the same name.
So basically I have
public class MyClass {
@Retryable(value = {Throwable.class}, exclude = { NotAcceptableException.class }, backoff = @Backoff(delay = 1000, multiplier = 2), maxAttemptsExpression = "5")
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
public <T> List<T> runQuery( Class<T> returnType, otherArgs ) { ...}
@Recover
private <T> List<T> recoverRunQuery(Exception ex, Class<T> returnType, otherArgs ) throws Exception { ... }
With spring-recover 2.0.0 that worked just fine but it fails with 2.0.4 which I've traced back to #328
This class is a helper for fetching data from numerous classes so T varies by the caller, so creating an instance for each would be a substantial code change. Hoping you have a suggestion on how I might make a recover method for that.
You just relied on a bug or rather some side-effect which didn't honor generics.
If you still insist on using those local <T>
, then consider to use this attribute on the @Retryable
:
* Name of method in this class to use for recover. Method had to be marked with
* {@link Recover} annotation.
* @return the name of recover method
String recover() default "";
instead of trying to trick reflection which we treat as a bug and therefore the fix you are mentioning.
How about to make your @Recover
method as Object
for return type (if something like List<?>
doesn't work)?
My point is that we need to agree that <T> List<T>
is local declaration and according to Java it is really not equal on different methods.
I wound up reworking the code to use https://github.com/spring-projects/spring-retry#using-recoverycallback
Small documentation issue on that page it should be Foo foo = template.execute(new RetryCallback<Foo, Exception>() {
So in case someone else is reading this bug the solution looks like
retryTemplate = RetryTemplate.builder()
.maxAttempts(maxAttempts)
.notRetryOn(List.of(NotAcceptableException.class, InstantiationException.class))
.customBackoff(BackOffPolicyBuilder.newBuilder().delay(delay).multiplier(multiplier).build())
.build();
return retryTemplate.execute(
(RetryCallback<List<T>, Exception>) context -> self.retryRunQuery(queryName, returnType, otherArgs),
context -> self.recoverQuery(context.getLastThrowable(), queryName)