diff --git a/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java b/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java index f4a3cb28..74c69d9c 100644 --- a/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java +++ b/src/main/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2022 the original author or authors. + * Copyright 2006-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,6 +53,7 @@ * @author Gary Russell * @author Artem Bilan * @author Gianluca Medici + * @author Lijinliang */ public class RecoverAnnotationRecoveryHandler implements MethodInvocationRecoverer { @@ -224,12 +225,12 @@ else if (recover != null && candidate.getReturnType().isAssignableFrom(failingMe * Returns {@code true} if the input methodReturnType is a direct match of the * failingMethodReturnType. Takes nested generics into consideration as well, while * deciding a match. - * @param methodReturnType - * @param failingMethodReturnType + * @param methodReturnType the method return type + * @param failingMethodReturnType the failing method return type * @return true if the parameterized return types match. * @since 1.3.2 */ - private boolean isParameterizedTypeAssignable(ParameterizedType methodReturnType, + private static boolean isParameterizedTypeAssignable(ParameterizedType methodReturnType, ParameterizedType failingMethodReturnType) { Type[] methodActualArgs = methodReturnType.getActualTypeArguments(); @@ -242,11 +243,18 @@ private boolean isParameterizedTypeAssignable(ParameterizedType methodReturnType Type methodArgType = methodActualArgs[i]; Type failingMethodArgType = failingMethodActualArgs[i]; if (methodArgType instanceof ParameterizedType && failingMethodArgType instanceof ParameterizedType) { - return isParameterizedTypeAssignable((ParameterizedType) methodArgType, - (ParameterizedType) failingMethodArgType); + if (!isParameterizedTypeAssignable((ParameterizedType) methodArgType, + (ParameterizedType) failingMethodArgType)) { + + return false; + } + } + else if (methodArgType instanceof Class && failingMethodArgType instanceof Class) { + if (!failingMethodArgType.equals(methodArgType)) { + return false; + } } - if (methodArgType instanceof Class && failingMethodArgType instanceof Class - && !failingMethodArgType.equals(methodArgType)) { + else if (!methodArgType.equals(failingMethodArgType)) { return false; } } diff --git a/src/test/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandlerTests.java b/src/test/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandlerTests.java index 62d8ddac..1607a861 100644 --- a/src/test/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandlerTests.java +++ b/src/test/java/org/springframework/retry/annotation/RecoverAnnotationRecoveryHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2022 the original author or authors. + * Copyright 2013-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -44,9 +46,31 @@ * @author Nathanaƫl Roberts * @author Maksim Kita * @author Gianluca Medici + * @author Lijinliang */ public class RecoverAnnotationRecoveryHandlerTests { + @Test + public void genericReturnTypesMatch() throws InvocationTargetException, IllegalAccessException { + Method isParameterizedTypeAssignable = + ReflectionUtils.findMethod(RecoverAnnotationRecoveryHandler.class, "isParameterizedTypeAssignable", + ParameterizedType.class, ParameterizedType.class); + isParameterizedTypeAssignable.setAccessible(true); + + assertThat(isParameterizedTypeAssignable.invoke(null, getGenericReturnTypeByName("m1"), + getGenericReturnTypeByName("m2"))).isEqualTo(Boolean.TRUE); + assertThat(isParameterizedTypeAssignable.invoke(null, getGenericReturnTypeByName("m2"), + getGenericReturnTypeByName("m2_1"))).isEqualTo(Boolean.FALSE); + assertThat(isParameterizedTypeAssignable.invoke(null, getGenericReturnTypeByName("m3"), + getGenericReturnTypeByName("m4"))).isEqualTo(Boolean.FALSE); + assertThat(isParameterizedTypeAssignable.invoke(null, getGenericReturnTypeByName("m5"), + getGenericReturnTypeByName("m6"))).isEqualTo(Boolean.TRUE); + } + + private static ParameterizedType getGenericReturnTypeByName(String name) { + return (ParameterizedType) ReflectionUtils.findMethod(ParameterTest.class, name).getGenericReturnType(); + } + @Test public void defaultRecoverMethod() { RecoverAnnotationRecoveryHandler handler = new RecoverAnnotationRecoveryHandler( @@ -293,6 +317,38 @@ public void recoverByComposedRetryableAnnotationName() { assertThat(handler.recover(new Object[] { "Kevin" }, new RuntimeException("Planned"))).isEqualTo(4); } + private static class ParameterTest { + + List m1() { + return null; + } + + List m2() { + return null; + } + + List m2_1() { + return null; + } + + Map, Byte> m3() { + return null; + } + + Map, Integer> m4() { + return null; + } + + Map, Byte> m5() { + return null; + } + + Map, Byte> m6() { + return null; + } + + } + private static class InAccessibleRecover { @Retryable