1919import java .lang .annotation .Annotation ;
2020import java .lang .reflect .AnnotatedElement ;
2121import java .lang .reflect .Method ;
22+ import java .lang .reflect .Modifier ;
2223import java .util .Collections ;
2324import java .util .LinkedHashMap ;
2425import java .util .List ;
2526import java .util .Map ;
27+ import java .util .Objects ;
2628import java .util .function .Predicate ;
2729import java .util .stream .Stream ;
2830
7173 * @author Rossen Stoyanchev
7274 * @author Sam Brannen
7375 * @author Olga Maciaszek-Sharma
76+ * @author Yongjun Hong
7477 * @since 3.1
7578 */
7679@ SuppressWarnings ("removal" )
@@ -183,13 +186,47 @@ protected boolean isHandler(Class<?> beanType) {
183186 * Uses type-level and method-level {@link RequestMapping @RequestMapping}
184187 * and {@link HttpExchange @HttpExchange} annotations to create the
185188 * {@link RequestMappingInfo}.
189+ * <p>For CGLIB proxy classes, additional validation is performed based on method visibility:
190+ * <ul>
191+ * <li>Private methods cannot be overridden and therefore cannot be used as handler methods.</li>
192+ * <li>Package-private methods from different packages are inaccessible and must be
193+ * changed to public or protected.</li>
194+ * </ul>
186195 * @return the created {@code RequestMappingInfo}, or {@code null} if the method
187196 * does not have a {@code @RequestMapping} or {@code @HttpExchange} annotation
188197 * @see #getCustomMethodCondition(Method)
189198 * @see #getCustomTypeCondition(Class)
190199 */
191200 @ Override
192201 protected @ Nullable RequestMappingInfo getMappingForMethod (Method method , Class <?> handlerType ) {
202+ int modifiers = method .getModifiers ();
203+
204+ if (isCglibProxy (handlerType )) {
205+ if (Modifier .isPrivate (modifiers )) {
206+ throw new IllegalStateException (
207+ "Private method [" + method + "] on CGLIB proxy class [" + handlerType .getName () +
208+ "] cannot be used as a request handler method because private methods cannot be overridden. " +
209+ "Change the method to non-private visibility or use interface-based JDK proxying instead." );
210+ }
211+
212+ if (!Modifier .isPublic (modifiers ) && !Modifier .isProtected (modifiers )) {
213+ Class <?> declaringClass = method .getDeclaringClass ();
214+ Package methodPackage = declaringClass .getPackage ();
215+ Package handlerPackage = handlerType .getPackage ();
216+
217+ if (Objects .equals (methodPackage , handlerPackage )) {
218+ String methodPackageName = (methodPackage != null ) ? methodPackage .getName () : "default package" ;
219+ String handlerPackageName = (handlerPackage != null ) ? handlerPackage .getName () : "default package" ;
220+
221+ throw new IllegalStateException (
222+ "Package-private method [" + method + "] on CGLIB proxy class [" + declaringClass .getName () +
223+ "] from package [" + methodPackageName + "] cannot be advised when used by handler class [" +
224+ handlerType .getName () + "] from package [" + handlerPackageName + "] because it is effectively private. " +
225+ "Either make the method public/protected or use interface-based JDK proxying instead." );
226+ }
227+ }
228+ }
229+
193230 RequestMappingInfo info = createRequestMappingInfo (method );
194231 if (info != null ) {
195232 RequestMappingInfo typeInfo = createRequestMappingInfo (handlerType );
@@ -207,6 +244,12 @@ protected boolean isHandler(Class<?> beanType) {
207244 return info ;
208245 }
209246
247+
248+ private boolean isCglibProxy (Class <?> beanType ) {
249+ return beanType .getName ().contains ("$$" );
250+ }
251+
252+
210253 @ Nullable String getPathPrefix (Class <?> handlerType ) {
211254 for (Map .Entry <String , Predicate <Class <?>>> entry : this .pathPrefixes .entrySet ()) {
212255 if (entry .getValue ().test (handlerType )) {
0 commit comments