1
1
/*
2
- * Copyright 2002-2019 the original author or authors.
2
+ * Copyright 2002-2024 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
22
22
import org .apache .commons .logging .Log ;
23
23
import org .apache .commons .logging .LogFactory ;
24
24
25
+ import org .springframework .aop .support .AopUtils ;
25
26
import org .springframework .lang .Nullable ;
26
27
import org .springframework .util .Assert ;
27
28
import org .springframework .util .ClassUtils ;
@@ -289,22 +290,14 @@ public static Object getField(@Nullable Object targetObject, @Nullable Class<?>
289
290
/**
290
291
* Invoke the setter method with the given {@code name} on the supplied
291
292
* target object with the supplied {@code value}.
292
- * <p>This method traverses the class hierarchy in search of the desired
293
- * method. In addition, an attempt will be made to make non-{@code public}
294
- * methods <em>accessible</em>, thus allowing one to invoke {@code protected},
295
- * {@code private}, and <em>package-private</em> setter methods.
296
- * <p>In addition, this method supports JavaBean-style <em>property</em>
297
- * names. For example, if you wish to set the {@code name} property on the
298
- * target object, you may pass either "name" or
299
- * "setName" as the method name.
293
+ * <p>This method delegates to
294
+ * {@link #invokeSetterMethod(Object, String, Object, Class)}, supplying
295
+ * {@code null} for the parameter type.
300
296
* @param target the target object on which to invoke the specified setter
301
297
* method
302
298
* @param name the name of the setter method to invoke or the corresponding
303
299
* property name
304
300
* @param value the value to provide to the setter method
305
- * @see ReflectionUtils#findMethod(Class, String, Class[])
306
- * @see ReflectionUtils#makeAccessible(Method)
307
- * @see ReflectionUtils#invokeMethod(Method, Object, Object[])
308
301
*/
309
302
public static void invokeSetterMethod (Object target , String name , Object value ) {
310
303
invokeSetterMethod (target , name , value , null );
@@ -317,19 +310,24 @@ public static void invokeSetterMethod(Object target, String name, Object value)
317
310
* method. In addition, an attempt will be made to make non-{@code public}
318
311
* methods <em>accessible</em>, thus allowing one to invoke {@code protected},
319
312
* {@code private}, and <em>package-private</em> setter methods.
320
- * <p>In addition, this method supports JavaBean-style <em>property</em>
321
- * names. For example, if you wish to set the {@code name} property on the
322
- * target object, you may pass either "name" or
323
- * "setName" as the method name.
313
+ * <p>This method also supports JavaBean-style <em>property</em> names. For
314
+ * example, if you wish to set the {@code name} property on the target object,
315
+ * you may pass either {@code "name"} or {@code "setName"} as the method name.
316
+ * <p>As of Spring Framework 6.2, if the supplied target object is a CGLIB
317
+ * proxy which does not intercept the setter method, the proxy will be
318
+ * {@linkplain AopTestUtils#getUltimateTargetObject unwrapped} allowing the
319
+ * setter method to be invoked directly on the ultimate target of the proxy.
324
320
* @param target the target object on which to invoke the specified setter
325
321
* method
326
322
* @param name the name of the setter method to invoke or the corresponding
327
323
* property name
328
324
* @param value the value to provide to the setter method
329
325
* @param type the formal parameter type declared by the setter method
326
+ * (may be {@code null} to indicate any type)
330
327
* @see ReflectionUtils#findMethod(Class, String, Class[])
331
328
* @see ReflectionUtils#makeAccessible(Method)
332
329
* @see ReflectionUtils#invokeMethod(Method, Object, Object[])
330
+ * @see AopTestUtils#getUltimateTargetObject(Object)
333
331
*/
334
332
public static void invokeSetterMethod (Object target , String name , @ Nullable Object value , @ Nullable Class <?> type ) {
335
333
Assert .notNull (target , "Target object must not be null" );
@@ -357,6 +355,14 @@ public static void invokeSetterMethod(Object target, String name, @Nullable Obje
357
355
safeToString (target ), value ));
358
356
}
359
357
358
+ if (springAopPresent ) {
359
+ // If the target is a CGLIB proxy which does not intercept the method, invoke the
360
+ // method on the ultimate target.
361
+ if (isCglibProxyThatDoesNotInterceptMethod (target , method )) {
362
+ target = AopTestUtils .getUltimateTargetObject (target );
363
+ }
364
+ }
365
+
360
366
ReflectionUtils .makeAccessible (method );
361
367
ReflectionUtils .invokeMethod (method , target , value );
362
368
}
@@ -368,10 +374,13 @@ public static void invokeSetterMethod(Object target, String name, @Nullable Obje
368
374
* method. In addition, an attempt will be made to make non-{@code public}
369
375
* methods <em>accessible</em>, thus allowing one to invoke {@code protected},
370
376
* {@code private}, and <em>package-private</em> getter methods.
371
- * <p>In addition, this method supports JavaBean-style <em>property</em>
372
- * names. For example, if you wish to get the {@code name} property on the
373
- * target object, you may pass either "name" or
374
- * "getName" as the method name.
377
+ * <p>This method also supports JavaBean-style <em>property</em> names. For
378
+ * example, if you wish to get the {@code name} property on the target object,
379
+ * you may pass either {@code "name"} or {@code "getName"} as the method name.
380
+ * <p>As of Spring Framework 6.2, if the supplied target object is a CGLIB
381
+ * proxy which does not intercept the getter method, the proxy will be
382
+ * {@linkplain AopTestUtils#getUltimateTargetObject unwrapped} allowing the
383
+ * getter method to be invoked directly on the ultimate target of the proxy.
375
384
* @param target the target object on which to invoke the specified getter
376
385
* method
377
386
* @param name the name of the getter method to invoke or the corresponding
@@ -380,6 +389,7 @@ public static void invokeSetterMethod(Object target, String name, @Nullable Obje
380
389
* @see ReflectionUtils#findMethod(Class, String, Class[])
381
390
* @see ReflectionUtils#makeAccessible(Method)
382
391
* @see ReflectionUtils#invokeMethod(Method, Object, Object[])
392
+ * @see AopTestUtils#getUltimateTargetObject(Object)
383
393
*/
384
394
@ Nullable
385
395
public static Object invokeGetterMethod (Object target , String name ) {
@@ -400,6 +410,14 @@ public static Object invokeGetterMethod(Object target, String name) {
400
410
"Could not find getter method '%s' on %s" , getterMethodName , safeToString (target )));
401
411
}
402
412
413
+ if (springAopPresent ) {
414
+ // If the target is a CGLIB proxy which does not intercept the method, invoke the
415
+ // method on the ultimate target.
416
+ if (isCglibProxyThatDoesNotInterceptMethod (target , method )) {
417
+ target = AopTestUtils .getUltimateTargetObject (target );
418
+ }
419
+ }
420
+
403
421
if (logger .isDebugEnabled ()) {
404
422
logger .debug (String .format ("Invoking getter method '%s' on %s" , getterMethodName , safeToString (target )));
405
423
}
@@ -418,10 +436,6 @@ public static Object invokeGetterMethod(Object target, String name) {
418
436
* @return the invocation result, if any
419
437
* @see #invokeMethod(Class, String, Object...)
420
438
* @see #invokeMethod(Object, Class, String, Object...)
421
- * @see MethodInvoker
422
- * @see ReflectionUtils#makeAccessible(Method)
423
- * @see ReflectionUtils#invokeMethod(Method, Object, Object[])
424
- * @see ReflectionUtils#handleReflectionException(Exception)
425
439
*/
426
440
@ Nullable
427
441
public static <T > T invokeMethod (Object target , String name , Object ... args ) {
@@ -441,10 +455,6 @@ public static <T> T invokeMethod(Object target, String name, Object... args) {
441
455
* @since 5.2
442
456
* @see #invokeMethod(Object, String, Object...)
443
457
* @see #invokeMethod(Object, Class, String, Object...)
444
- * @see MethodInvoker
445
- * @see ReflectionUtils#makeAccessible(Method)
446
- * @see ReflectionUtils#invokeMethod(Method, Object, Object[])
447
- * @see ReflectionUtils#handleReflectionException(Exception)
448
458
*/
449
459
@ Nullable
450
460
public static <T > T invokeMethod (Class <?> targetClass , String name , Object ... args ) {
@@ -459,6 +469,10 @@ public static <T> T invokeMethod(Class<?> targetClass, String name, Object... ar
459
469
* method. In addition, an attempt will be made to make non-{@code public}
460
470
* methods <em>accessible</em>, thus allowing one to invoke {@code protected},
461
471
* {@code private}, and <em>package-private</em> methods.
472
+ * <p>As of Spring Framework 6.2, if the supplied target object is a CGLIB
473
+ * proxy which does not intercept the method, the proxy will be
474
+ * {@linkplain AopTestUtils#getUltimateTargetObject unwrapped} allowing the
475
+ * method to be invoked directly on the ultimate target of the proxy.
462
476
* @param targetObject the target object on which to invoke the method; may
463
477
* be {@code null} if the method is static
464
478
* @param targetClass the target class on which to invoke the method; may
@@ -471,8 +485,8 @@ public static <T> T invokeMethod(Class<?> targetClass, String name, Object... ar
471
485
* @see #invokeMethod(Class, String, Object...)
472
486
* @see MethodInvoker
473
487
* @see ReflectionUtils#makeAccessible(Method)
474
- * @see ReflectionUtils#invokeMethod(Method, Object, Object[])
475
488
* @see ReflectionUtils#handleReflectionException(Exception)
489
+ * @see AopTestUtils#getUltimateTargetObject(Object)
476
490
*/
477
491
@ SuppressWarnings ("unchecked" )
478
492
@ Nullable
@@ -493,6 +507,15 @@ public static <T> T invokeMethod(@Nullable Object targetObject, @Nullable Class<
493
507
methodInvoker .setArguments (args );
494
508
methodInvoker .prepare ();
495
509
510
+ if (targetObject != null && springAopPresent ) {
511
+ // If the target is a CGLIB proxy which does not intercept the method, invoke the
512
+ // method on the ultimate target.
513
+ if (isCglibProxyThatDoesNotInterceptMethod (targetObject , methodInvoker .getPreparedMethod ())) {
514
+ targetObject = AopTestUtils .getUltimateTargetObject (targetObject );
515
+ methodInvoker .setTargetObject (targetObject );
516
+ }
517
+ }
518
+
496
519
if (logger .isDebugEnabled ()) {
497
520
logger .debug (String .format ("Invoking method '%s' on %s or %s with arguments %s" , name ,
498
521
safeToString (targetObject ), safeToString (targetClass ), ObjectUtils .nullSafeToString (args )));
@@ -520,4 +543,13 @@ private static String safeToString(@Nullable Class<?> clazz) {
520
543
return String .format ("target class [%s]" , (clazz != null ? clazz .getName () : null ));
521
544
}
522
545
546
+ /**
547
+ * Determine if the supplied target object is a CBLIB proxy that does not intercept the
548
+ * supplied method.
549
+ * @since 6.2
550
+ */
551
+ private static boolean isCglibProxyThatDoesNotInterceptMethod (Object target , Method method ) {
552
+ return (AopUtils .isCglibProxy (target ) && !method .getDeclaringClass ().equals (target .getClass ()));
553
+ }
554
+
523
555
}
0 commit comments