Skip to content

Commit 55a0906

Browse files
committed
Merge branch '6.2.x'
2 parents 0c73901 + 1d7cb4f commit 55a0906

File tree

6 files changed

+98
-25
lines changed

6 files changed

+98
-25
lines changed

spring-core/src/main/java/org/springframework/core/annotation/AnnotatedMethod.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -212,8 +212,8 @@ private boolean isOverrideFor(Method candidate) {
212212

213213
@Override
214214
public boolean equals(@Nullable Object other) {
215-
return (this == other || (other != null && getClass() == other.getClass() &&
216-
this.method.equals(((AnnotatedMethod) other).method)));
215+
return (this == other || (other instanceof AnnotatedMethod otherHandlerMethod &&
216+
this.method.equals(otherHandlerMethod.method)));
217217
}
218218

219219
@Override

spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,13 @@ protected HandlerMethod(HandlerMethod handlerMethod) {
176176
}
177177

178178
/**
179-
* Re-create HandlerMethod with additional input.
179+
* Re-create new HandlerMethod instance that copies the given HandlerMethod
180+
* but replaces the handler, and optionally checks for the presence of
181+
* validation annotations.
182+
* <p>Subclasses can override this to ensure that a HandlerMethod is of the
183+
* same type if re-created.
180184
*/
181-
private HandlerMethod(HandlerMethod handlerMethod, @Nullable Object handler, boolean initValidateFlags) {
185+
protected HandlerMethod(HandlerMethod handlerMethod, @Nullable Object handler, boolean initValidateFlags) {
182186
super(handlerMethod);
183187
this.bean = (handler != null ? handler : handlerMethod.bean);
184188
this.beanFactory = handlerMethod.beanFactory;
@@ -192,7 +196,8 @@ private HandlerMethod(HandlerMethod handlerMethod, @Nullable Object handler, boo
192196
handlerMethod.validateReturnValue);
193197
this.responseStatus = handlerMethod.responseStatus;
194198
this.responseStatusReason = handlerMethod.responseStatusReason;
195-
this.resolvedFromHandlerMethod = handlerMethod;
199+
this.resolvedFromHandlerMethod = (handlerMethod.resolvedFromHandlerMethod != null ?
200+
handlerMethod.resolvedFromHandlerMethod : handlerMethod);
196201
this.description = handlerMethod.toString();
197202
}
198203

spring-web/src/main/java/org/springframework/web/util/ServletRequestPathUtils.java

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -179,6 +179,25 @@ public static boolean hasCachedPath(ServletRequest request) {
179179
request.getAttribute(UrlPathHelper.PATH_ATTRIBUTE) != null);
180180
}
181181

182+
/**
183+
* Check if the Servlet is mapped by a path prefix, and if so return that
184+
* path prefix.
185+
* @param request the current request
186+
* @return the prefix, or {@code null} if the Servlet is not mapped by prefix
187+
* @since 6.2.3
188+
*/
189+
public static @Nullable String getServletPathPrefix(HttpServletRequest request) {
190+
HttpServletMapping mapping = (HttpServletMapping) request.getAttribute(RequestDispatcher.INCLUDE_MAPPING);
191+
mapping = (mapping != null ? mapping : request.getHttpServletMapping());
192+
if (ObjectUtils.nullSafeEquals(mapping.getMappingMatch(), MappingMatch.PATH)) {
193+
String servletPath = (String) request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE);
194+
servletPath = (servletPath != null ? servletPath : request.getServletPath());
195+
servletPath = (servletPath.endsWith("/") ? servletPath.substring(0, servletPath.length() - 1) : servletPath);
196+
return servletPath;
197+
}
198+
return null;
199+
}
200+
182201

183202
/**
184203
* Simple wrapper around the default {@link RequestPath} implementation that
@@ -251,21 +270,11 @@ public static RequestPath parse(HttpServletRequest request) {
251270
String requestUri = (String) request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE);
252271
requestUri = (requestUri != null ? requestUri : request.getRequestURI());
253272
String servletPathPrefix = getServletPathPrefix(request);
254-
return (StringUtils.hasText(servletPathPrefix) ?
255-
new ServletRequestPath(new PathElements(requestUri, request.getContextPath(), servletPathPrefix)) :
256-
RequestPath.parse(requestUri, request.getContextPath()));
257-
}
258-
259-
private static @Nullable String getServletPathPrefix(HttpServletRequest request) {
260-
HttpServletMapping mapping = (HttpServletMapping) request.getAttribute(RequestDispatcher.INCLUDE_MAPPING);
261-
mapping = (mapping != null ? mapping : request.getHttpServletMapping());
262-
if (ObjectUtils.nullSafeEquals(mapping.getMappingMatch(), MappingMatch.PATH)) {
263-
String servletPath = (String) request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE);
264-
servletPath = (servletPath != null ? servletPath : request.getServletPath());
265-
servletPath = (servletPath.endsWith("/") ? servletPath.substring(0, servletPath.length() - 1) : servletPath);
266-
return UriUtils.encodePath(servletPath, StandardCharsets.UTF_8);
273+
if (!StringUtils.hasLength(servletPathPrefix)) {
274+
return RequestPath.parse(requestUri, request.getContextPath());
267275
}
268-
return null;
276+
servletPathPrefix = UriUtils.encodePath(servletPathPrefix, StandardCharsets.UTF_8);
277+
return new ServletRequestPath(new PathElements(requestUri, request.getContextPath(), servletPathPrefix));
269278
}
270279

271280
record PathElements(String rawPath, @Nullable String contextPath, String servletPathPrefix) {

spring-web/src/test/java/org/springframework/web/method/HandlerMethodTests.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import jakarta.validation.constraints.Size;
2626
import org.junit.jupiter.api.Test;
2727

28+
import org.springframework.context.support.StaticApplicationContext;
2829
import org.springframework.util.ClassUtils;
2930
import org.springframework.validation.annotation.Validated;
3031

@@ -79,6 +80,23 @@ void createWithResolvedBeanSameInstance() {
7980
assertThat(handlerMethod.createWithResolvedBean()).isSameAs(handlerMethod);
8081
}
8182

83+
@Test
84+
void resolvedFromHandlerMethod() {
85+
StaticApplicationContext context = new StaticApplicationContext();
86+
context.registerSingleton("myClass", MyClass.class);
87+
88+
MyClass target = new MyClass();
89+
Method method = ClassUtils.getMethod(target.getClass(), "addPerson", (Class<?>[]) null);
90+
91+
HandlerMethod hm1 = new HandlerMethod("myClass", context.getBeanFactory(), method);
92+
HandlerMethod hm2 = hm1.createWithValidateFlags();
93+
HandlerMethod hm3 = hm2.createWithResolvedBean();
94+
95+
assertThat(hm1.getResolvedFromHandlerMethod()).isNull();
96+
assertThat(hm2.getResolvedFromHandlerMethod()).isSameAs(hm1);
97+
assertThat(hm3.getResolvedFromHandlerMethod()).isSameAs(hm1);
98+
}
99+
82100
private static void testValidateArgs(Object target, List<String> methodNames, boolean expected) {
83101
for (String methodName : methodNames) {
84102
assertThat(getHandlerMethod(target, methodName).shouldValidateArguments()).isEqualTo(expected);

spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -605,9 +605,6 @@ public void register(T mapping, Object handler, Method method) {
605605
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
606606
validateMethodMapping(handlerMethod, mapping);
607607

608-
// Enable method validation, if applicable
609-
handlerMethod = handlerMethod.createWithValidateFlags();
610-
611608
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
612609
for (String path : directPaths) {
613610
this.pathLookup.add(path, mapping);
@@ -626,6 +623,10 @@ public void register(T mapping, Object handler, Method method) {
626623
this.corsLookup.put(handlerMethod, corsConfig);
627624
}
628625

626+
// Init validation flags
627+
// We do this strictly after using the original instance in the CORS lookups
628+
handlerMethod = handlerMethod.createWithValidateFlags();
629+
629630
this.registry.put(mapping,
630631
new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
631632
}

spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.java

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -292,6 +292,23 @@ void getCorsConfigWithBeanNameHandler() throws Exception {
292292
HandlerMethod handlerMethod = this.mapping.getHandlerInternal(new MockHttpServletRequest("GET", key));
293293
}
294294

295+
@Test
296+
void registerCustomHandlerMethod() throws Exception {
297+
this.mapping.setCustomerHandlerMethod(true);
298+
this.mapping.registerMapping("/foo", this.handler, this.handler.getClass().getMethod("corsHandlerMethod"));
299+
300+
MockHttpServletRequest request = new MockHttpServletRequest("OPTIONS", "/foo");
301+
request.addParameter("abort", "true");
302+
request.addHeader(HttpHeaders.ORIGIN, "https://domain.com");
303+
request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET");
304+
305+
MockHttpServletResponse response = new MockHttpServletResponse();
306+
307+
HandlerExecutionChain chain = this.mapping.getHandler(request);
308+
309+
assertThat(chain).isNotNull();
310+
assertThat(response.getStatus()).isEqualTo(200);
311+
}
295312

296313

297314
private static class MyHandlerMethodMapping extends AbstractHandlerMethodMapping<String> {
@@ -302,6 +319,8 @@ private static class MyHandlerMethodMapping extends AbstractHandlerMethodMapping
302319

303320
private final List<String> matches = new ArrayList<>();
304321

322+
private boolean customerHandlerMethod;
323+
305324
public MyHandlerMethodMapping() {
306325
setHandlerMethodMappingNamingStrategy(new SimpleMappingNamingStrategy());
307326
}
@@ -326,6 +345,16 @@ protected String getMappingForMethod(Method method, Class<?> handlerType) {
326345
return methodName.startsWith("handler") ? methodName : null;
327346
}
328347

348+
public void setCustomerHandlerMethod(boolean customerHandlerMethod) {
349+
this.customerHandlerMethod = customerHandlerMethod;
350+
}
351+
352+
@Override
353+
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
354+
return (this.customerHandlerMethod ?
355+
new CustomHandlerMethod(handler, method) : super.createHandlerMethod(handler, method));
356+
}
357+
329358
@Override
330359
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, String mapping) {
331360
CrossOrigin crossOrigin = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);
@@ -355,6 +384,7 @@ protected Comparator<String> getMappingComparator(HttpServletRequest request) {
355384

356385
}
357386

387+
358388
private static class SimpleMappingNamingStrategy implements HandlerMethodMappingNamingStrategy<String> {
359389

360390
@Override
@@ -363,6 +393,16 @@ public String getName(HandlerMethod handlerMethod, String mapping) {
363393
}
364394
}
365395

396+
397+
private static class CustomHandlerMethod extends HandlerMethod {
398+
399+
public CustomHandlerMethod(Object bean, Method method) {
400+
super(bean, method);
401+
}
402+
403+
}
404+
405+
366406
@Controller
367407
static class MyHandler {
368408

0 commit comments

Comments
 (0)