5353import org .springframework .web .service .annotation .PostExchange ;
5454import org .springframework .web .service .annotation .PutExchange ;
5555import org .springframework .web .testfixture .http .server .reactive .MockServerHttpRequest ;
56+ import org .springframework .web .testfixture .method .PackagePrivateMethodController ;
5657import org .springframework .web .testfixture .server .MockServerWebExchange ;
5758import org .springframework .web .util .pattern .PathPattern ;
5859import org .springframework .web .util .pattern .PathPatternParser ;
6263import static org .mockito .Mockito .mock ;
6364import static org .springframework .web .bind .annotation .RequestMethod .POST ;
6465import static org .springframework .web .reactive .result .method .RequestMappingInfo .paths ;
65- import static org .springframework .web .testfixture .method .VisibilityTestHandler .PackagePrivateController ;
66- import static org .springframework .web .testfixture .method .VisibilityTestHandler .ProtectedController ;
6766
6867/**
6968 * Tests for {@link RequestMappingHandlerMapping}.
@@ -414,25 +413,26 @@ void requestBodyAnnotationFromImplementationOverridesInterface() {
414413 assertThat (matchingInfo ).isEqualTo (paths (path ).methods (POST ).consumes (mediaType .toString ()).build ());
415414 }
416415
417- @ Test
416+ @ Test // gh-35352
418417 void privateMethodOnCglibProxyShouldThrowException () throws Exception {
419418 RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping ();
420419
421420 Class <?> handlerType = PrivateMethodController .class ;
422- Method method = handlerType . getDeclaredMethod ( "privateMethod" ) ;
423-
421+ String methodName = "privateMethod" ;
422+ Method method = handlerType . getDeclaredMethod ( methodName );
424423 Class <?> proxyClass = createProxyClass (handlerType );
425424
426425 assertThatIllegalStateException ()
427426 .isThrownBy (() -> mapping .getMappingForMethod (method , proxyClass ))
428- .withMessageContainingAll (
429- "Private method [privateMethod]" ,
430- "cannot be used as a request handler method"
431- );
427+ .withMessage ("""
428+ Private method [%s] on CGLIB proxy class [%s] cannot be used as a request \
429+ handler method, because private methods cannot be overridden. \
430+ Change the method to non-private visibility, or use interface-based JDK \
431+ proxying instead.""" , methodName , proxyClass .getName ());
432432 }
433433
434- @ Test
435- void protectedMethodShouldNotThrowException () throws Exception {
434+ @ Test // gh-35352
435+ void protectedMethodOnCglibProxyShouldNotThrowException () throws Exception {
436436 RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping ();
437437
438438 Class <?> handlerType = ProtectedMethodController .class ;
@@ -444,63 +444,33 @@ void protectedMethodShouldNotThrowException() throws Exception {
444444 assertThat (info .getPatternsCondition ().getDirectPaths ()).containsOnly ("/protected" );
445445 }
446446
447- @ Test
448- void differentPackagePackagePrivateMethodShouldThrowException () throws Exception {
447+ @ Test // gh-35352
448+ void differentPackagePackagePrivateMethodOnCglibProxyShouldThrowException () throws Exception {
449449 RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping ();
450450
451- Class <?> handlerType = ControllerWithPackagePrivateClass .class ;
452- Method method = PackagePrivateController .class .getDeclaredMethod ("packagePrivateMethod" );
453-
451+ Class <?> handlerType = LocalPackagePrivateMethodControllerSubclass .class ;
452+ Class <?> declaringClass = PackagePrivateMethodController .class ;
453+ String methodName = "packagePrivateMethod" ;
454+ Method method = declaringClass .getDeclaredMethod (methodName );
454455 Class <?> proxyClass = createProxyClass (handlerType );
455456
456457 assertThatIllegalStateException ()
457458 .isThrownBy (() -> mapping .getMappingForMethod (method , proxyClass ))
458- .withMessageContainingAll (
459- "Package-private method [packagePrivateMethod]" ,
460- "cannot be advised when used by handler class"
461- );
462- }
463-
464- @ Test
465- void differentPackageProtectedMethodShouldNotThrowException () throws Exception {
466- RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping ();
467-
468- Class <?> handlerType = ControllerWithProtectedClass .class ;
469- Method method = ProtectedController .class .getDeclaredMethod ("protectedMethod" );
470-
471- Class <?> proxyClass = createProxyClass (handlerType );
472-
473- RequestMappingInfo info = mapping .getMappingForMethod (method , proxyClass );
474- assertThat (info .getPatternsCondition ().getDirectPaths ()).containsOnly ("/protected" );
459+ .withMessage ("""
460+ Package-private method [%s] declared in class [%s] cannot be advised by \
461+ CGLIB-proxied handler class [%s], because it is effectively private. Either \
462+ make the method public or protected, or use interface-based JDK proxying instead.""" ,
463+ methodName , declaringClass .getName (), proxyClass .getName ());
475464 }
476465
477466
478- private Class <?> createProxyClass (Class <?> targetClass ) {
467+ private static Class <?> createProxyClass (Class <?> targetClass ) {
479468 Enhancer enhancer = new Enhancer ();
480469 enhancer .setSuperclass (targetClass );
481470 enhancer .setCallbackTypes (new Class []{NoOp .class });
482-
483471 return enhancer .createClass ();
484472 }
485473
486- @ Controller
487- static class PrivateMethodController {
488- @ RequestMapping ("/private" )
489- private void privateMethod () {}
490- }
491-
492- @ Controller
493- static class ProtectedMethodController {
494- @ RequestMapping ("/protected" )
495- protected void protectedMethod () {}
496- }
497-
498- @ Controller
499- static class ControllerWithPackagePrivateClass extends PackagePrivateController { }
500-
501- @ Controller
502- static class ControllerWithProtectedClass extends ProtectedController { }
503-
504474 private RequestMappingInfo assertComposedAnnotationMapping (RequestMethod requestMethod ) {
505475 String methodName = requestMethod .name ().toLowerCase ();
506476 String path = "/" + methodName ;
@@ -705,4 +675,21 @@ public void post(@RequestBody(required = true) Foo foo) {}
705675 @interface ExtraHttpExchange {
706676 }
707677
678+ @ Controller
679+ static class PrivateMethodController {
680+
681+ @ RequestMapping ("/private" )
682+ private void privateMethod () {}
683+ }
684+
685+ @ Controller
686+ static class ProtectedMethodController {
687+
688+ @ RequestMapping ("/protected" )
689+ protected void protectedMethod () {}
690+ }
691+
692+ static class LocalPackagePrivateMethodControllerSubclass extends PackagePrivateMethodController {
693+ }
694+
708695}
0 commit comments