diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedMethod.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedMethod.java index b2578a4a73..5335da3ccd 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedMethod.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -212,8 +212,8 @@ public class AnnotatedMethod { @Override public boolean equals(@Nullable Object other) { - return (this == other || (other != null && getClass() == other.getClass() && - this.method.equals(((AnnotatedMethod) other).method))); + return (this == other || (other instanceof AnnotatedMethod otherHandlerMethod && + this.method.equals(otherHandlerMethod.method))); } @Override 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 2db063ab7d..2db937f766 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 @@ -176,9 +176,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;
@@ -192,7 +196,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/main/java/org/springframework/web/util/ServletRequestPathUtils.java b/spring-web/src/main/java/org/springframework/web/util/ServletRequestPathUtils.java
index 06683bdb75..4cadd3daf9 100644
--- a/spring-web/src/main/java/org/springframework/web/util/ServletRequestPathUtils.java
+++ b/spring-web/src/main/java/org/springframework/web/util/ServletRequestPathUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2024 the original author or authors.
+ * Copyright 2002-2025 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.
@@ -179,6 +179,25 @@ public abstract class ServletRequestPathUtils {
request.getAttribute(UrlPathHelper.PATH_ATTRIBUTE) != null);
}
+ /**
+ * Check if the Servlet is mapped by a path prefix, and if so return that
+ * path prefix.
+ * @param request the current request
+ * @return the prefix, or {@code null} if the Servlet is not mapped by prefix
+ * @since 6.2.3
+ */
+ public static @Nullable String getServletPathPrefix(HttpServletRequest request) {
+ HttpServletMapping mapping = (HttpServletMapping) request.getAttribute(RequestDispatcher.INCLUDE_MAPPING);
+ mapping = (mapping != null ? mapping : request.getHttpServletMapping());
+ if (ObjectUtils.nullSafeEquals(mapping.getMappingMatch(), MappingMatch.PATH)) {
+ String servletPath = (String) request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE);
+ servletPath = (servletPath != null ? servletPath : request.getServletPath());
+ servletPath = (servletPath.endsWith("/") ? servletPath.substring(0, servletPath.length() - 1) : servletPath);
+ return servletPath;
+ }
+ return null;
+ }
+
/**
* Simple wrapper around the default {@link RequestPath} implementation that
@@ -251,21 +270,11 @@ public abstract class ServletRequestPathUtils {
String requestUri = (String) request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE);
requestUri = (requestUri != null ? requestUri : request.getRequestURI());
String servletPathPrefix = getServletPathPrefix(request);
- return (StringUtils.hasText(servletPathPrefix) ?
- new ServletRequestPath(new PathElements(requestUri, request.getContextPath(), servletPathPrefix)) :
- RequestPath.parse(requestUri, request.getContextPath()));
- }
-
- private static @Nullable String getServletPathPrefix(HttpServletRequest request) {
- HttpServletMapping mapping = (HttpServletMapping) request.getAttribute(RequestDispatcher.INCLUDE_MAPPING);
- mapping = (mapping != null ? mapping : request.getHttpServletMapping());
- if (ObjectUtils.nullSafeEquals(mapping.getMappingMatch(), MappingMatch.PATH)) {
- String servletPath = (String) request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE);
- servletPath = (servletPath != null ? servletPath : request.getServletPath());
- servletPath = (servletPath.endsWith("/") ? servletPath.substring(0, servletPath.length() - 1) : servletPath);
- return UriUtils.encodePath(servletPath, StandardCharsets.UTF_8);
+ if (!StringUtils.hasLength(servletPathPrefix)) {
+ return RequestPath.parse(requestUri, request.getContextPath());
}
- return null;
+ servletPathPrefix = UriUtils.encodePath(servletPathPrefix, StandardCharsets.UTF_8);
+ return new ServletRequestPath(new PathElements(requestUri, request.getContextPath(), servletPathPrefix));
}
record PathElements(String rawPath, @Nullable String contextPath, String servletPathPrefix) {
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 660dfa06d6..8d1f825028 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