diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java index 62caefd2f1..3ec91acdeb 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/handler/AbstractHandlerMethodMapping.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. @@ -636,9 +636,6 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap HandlerMethod handlerMethod = createHandlerMethod(handler, method); validateMethodMapping(handlerMethod, mapping); - // Enable method validation, if applicable - handlerMethod = handlerMethod.createWithValidateFlags(); - Set directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping); for (String path : directPaths) { this.pathLookup.add(path, mapping); @@ -657,6 +654,10 @@ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMap this.corsLookup.put(handlerMethod, corsConfig); } + // Init validation flags + // We do this strictly after using the original instance in the CORS lookups + handlerMethod = handlerMethod.createWithValidateFlags(); + this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null)); } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.java index 75d7eca575..9e3b3cf415 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/handler/HandlerMethodMappingTests.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. @@ -292,6 +292,23 @@ public class HandlerMethodMappingTests { HandlerMethod handlerMethod = this.mapping.getHandlerInternal(new MockHttpServletRequest("GET", key)); } + @Test + void registerCustomHandlerMethod() throws Exception { + this.mapping.setCustomerHandlerMethod(true); + this.mapping.registerMapping("/foo", this.handler, this.handler.getClass().getMethod("corsHandlerMethod")); + + MockHttpServletRequest request = new MockHttpServletRequest("OPTIONS", "/foo"); + request.addParameter("abort", "true"); + request.addHeader(HttpHeaders.ORIGIN, "https://domain.com"); + request.addHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET"); + + MockHttpServletResponse response = new MockHttpServletResponse(); + + HandlerExecutionChain chain = this.mapping.getHandler(request); + + assertThat(chain).isNotNull(); + assertThat(response.getStatus()).isEqualTo(200); + } private static class MyHandlerMethodMapping extends AbstractHandlerMethodMapping { @@ -302,6 +319,8 @@ public class HandlerMethodMappingTests { private final List matches = new ArrayList<>(); + private boolean customerHandlerMethod; + public MyHandlerMethodMapping() { setHandlerMethodMappingNamingStrategy(new SimpleMappingNamingStrategy()); } @@ -326,6 +345,16 @@ public class HandlerMethodMappingTests { return methodName.startsWith("handler") ? methodName : null; } + public void setCustomerHandlerMethod(boolean customerHandlerMethod) { + this.customerHandlerMethod = customerHandlerMethod; + } + + @Override + protected HandlerMethod createHandlerMethod(Object handler, Method method) { + return (this.customerHandlerMethod ? + new CustomHandlerMethod(handler, method) : super.createHandlerMethod(handler, method)); + } + @Override protected CorsConfiguration initCorsConfiguration(Object handler, Method method, String mapping) { CrossOrigin crossOrigin = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class); @@ -355,6 +384,7 @@ public class HandlerMethodMappingTests { } + private static class SimpleMappingNamingStrategy implements HandlerMethodMappingNamingStrategy { @Override @@ -363,6 +393,16 @@ public class HandlerMethodMappingTests { } } + + private static class CustomHandlerMethod extends HandlerMethod { + + public CustomHandlerMethod(Object bean, Method method) { + super(bean, method); + } + + } + + @Controller static class MyHandler {