From 56c4d2d4ca371a9a09c654f7eb6ddbfbdcdf0488 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Fri, 7 Feb 2025 13:01:04 +0000 Subject: [PATCH] Improve HandlerMethod#resolvedFromHandlerMethod initialization Ensure the original instance is always the one returned no matter how many times the HandlerMethod is re-created. Make the constructor protected to allow subclasses to re-create the HandlerMethod as the concrete subclass. See gh-34375 --- .../web/method/HandlerMethod.java | 11 ++++++++--- .../web/method/HandlerMethodTests.java | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java index 0ef3a1bc001..44c7cd2066e 100644 --- a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java +++ b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java @@ -181,9 +181,13 @@ public class HandlerMethod extends AnnotatedMethod { } /** - * Re-create HandlerMethod with additional input. + * Re-create new HandlerMethod instance that copies the given HandlerMethod + * but replaces the handler, and optionally checks for the presence of + * validation annotations. + *

Subclasses can override this to ensure that a HandlerMethod is of the + * same type if re-created. */ - private HandlerMethod(HandlerMethod handlerMethod, @Nullable Object handler, boolean initValidateFlags) { + protected HandlerMethod(HandlerMethod handlerMethod, @Nullable Object handler, boolean initValidateFlags) { super(handlerMethod); this.bean = (handler != null ? handler : handlerMethod.bean); this.beanFactory = handlerMethod.beanFactory; @@ -197,7 +201,8 @@ public class HandlerMethod extends AnnotatedMethod { handlerMethod.validateReturnValue); this.responseStatus = handlerMethod.responseStatus; this.responseStatusReason = handlerMethod.responseStatusReason; - this.resolvedFromHandlerMethod = handlerMethod; + this.resolvedFromHandlerMethod = (handlerMethod.resolvedFromHandlerMethod != null ? + handlerMethod.resolvedFromHandlerMethod : handlerMethod); this.description = handlerMethod.toString(); } diff --git a/spring-web/src/test/java/org/springframework/web/method/HandlerMethodTests.java b/spring-web/src/test/java/org/springframework/web/method/HandlerMethodTests.java index 660dfa06d6e..8d1f825028c 100644 --- a/spring-web/src/test/java/org/springframework/web/method/HandlerMethodTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/HandlerMethodTests.java @@ -25,6 +25,7 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.Size; import org.junit.jupiter.api.Test; +import org.springframework.context.support.StaticApplicationContext; import org.springframework.util.ClassUtils; import org.springframework.validation.annotation.Validated; @@ -79,6 +80,23 @@ class HandlerMethodTests { assertThat(handlerMethod.createWithResolvedBean()).isSameAs(handlerMethod); } + @Test + void resolvedFromHandlerMethod() { + StaticApplicationContext context = new StaticApplicationContext(); + context.registerSingleton("myClass", MyClass.class); + + MyClass target = new MyClass(); + Method method = ClassUtils.getMethod(target.getClass(), "addPerson", (Class[]) null); + + HandlerMethod hm1 = new HandlerMethod("myClass", context.getBeanFactory(), method); + HandlerMethod hm2 = hm1.createWithValidateFlags(); + HandlerMethod hm3 = hm2.createWithResolvedBean(); + + assertThat(hm1.getResolvedFromHandlerMethod()).isNull(); + assertThat(hm2.getResolvedFromHandlerMethod()).isSameAs(hm1); + assertThat(hm3.getResolvedFromHandlerMethod()).isSameAs(hm1); + } + private static void testValidateArgs(Object target, List methodNames, boolean expected) { for (String methodName : methodNames) { assertThat(getHandlerMethod(target, methodName).shouldValidateArguments()).isEqualTo(expected);