diff --git a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java index c87cc74729a5..090a16167790 100644 --- a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java +++ b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java @@ -18,6 +18,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.Map; import java.util.Objects; @@ -35,6 +36,7 @@ import reactor.core.publisher.Mono; import reactor.core.publisher.SynchronousSink; +import org.springframework.aop.support.AopUtils; import org.springframework.context.MessageSource; import org.springframework.core.CoroutinesUtils; import org.springframework.core.DefaultParameterNameDiscoverer; @@ -58,6 +60,7 @@ * @author Rossen Stoyanchev * @author Juergen Hoeller * @author Sebastien Deleuze + * @author Yongjun Hong * @since 3.1 */ public class InvocableHandlerMethod extends HandlerMethod { @@ -246,6 +249,16 @@ public void setMethodValidator(@Nullable MethodValidator methodValidator) { */ protected @Nullable Object doInvoke(@Nullable Object... args) throws Exception { Method method = getBridgedMethod(); + Object bean = getBean(); + + if (AopUtils.isCglibProxy(bean) && Modifier.isPrivate(method.getModifiers())) { + throw new IllegalStateException( + "Cannot invoke private method [" + method.getName() + "] on a CGLIB proxy. " + + "Handler methods on proxied components must be public or protected. " + + "Change method visibility or use interface-based JDK proxies if applicable." + ); + } + try { if (KotlinDetector.isKotlinType(method.getDeclaringClass())) { if (KotlinDetector.isSuspendingFunction(method)) { diff --git a/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java b/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java index a2d06b9bb1d6..484f799d7088 100644 --- a/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/support/InvocableHandlerMethodTests.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.aop.framework.ProxyFactory; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; @@ -42,6 +43,7 @@ * Tests for {@link InvocableHandlerMethod}. * * @author Rossen Stoyanchev + * @author Yongjun Hong */ class InvocableHandlerMethodTests { @@ -168,6 +170,21 @@ public void invocationErrorMessage() { .withMessageContaining("Illegal argument"); } + @Test + void testPrivateMethodOnCglibProxyThrowsException() throws Exception { + TestController target = new TestController(); + ProxyFactory proxyFactory = new ProxyFactory(target); + proxyFactory.setProxyTargetClass(true); + Object proxy = proxyFactory.getProxy(); + + Method privateMethod = TestController.class.getDeclaredMethod("privateMethod"); + InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(proxy, privateMethod); + + assertThatIllegalStateException() + .isThrownBy(() -> handlerMethod.invokeForRequest(null, null)) + .withMessageContaining("Cannot invoke private method [privateMethod] on a CGLIB proxy"); + } + private InvocableHandlerMethod getInvocable(Class... argTypes) { Method method = ResolvableMethod.on(Handler.class).argTypes(argTypes).resolveMethod(); InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(new Handler(), method); @@ -216,4 +233,12 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m } } + private static class TestController { + public TestController() { + // Default constructor for proxy creation + } + + private void privateMethod() { } + } + }