29
29
public class ProxyUtil {
30
30
private static final String SPRING_ADVISED_CLASS_NAME = "org.springframework.aop.framework.Advised" ;
31
31
private static final String SPRING_SPRINGPROXY_CLASS_NAME = "org.springframework.aop.SpringProxy" ;
32
+ private static final String SPRING_SINGLETONTARGETSOURCE_CLASS_NAME = "org.springframework.aop.target.SingletonTargetSource" ;
33
+ private static final String SPRING_TARGETCLASSAWARE_CLASS_NAME = "org.springframework.aop.TargetClassAware" ;
32
34
33
35
/**
34
- * Get the ultimate <em>target</em> object of the supplied {@code candidate}
35
- * object, unwrapping not only a top-level proxy but also any number of
36
- * nested proxies.
37
- * <p>If the supplied {@code candidate} is a Spring proxy, the ultimate target of all
38
- * nested proxies will be returned; otherwise, the {@code candidate}
39
- * will be returned <em>as is</em>.
40
- * @param candidate the instance to check (potentially a Spring AOP proxy;
41
- * never {@code null})
42
- * @return the target object or the {@code candidate} (never {@code null})
43
- * @throws IllegalStateException if an error occurs while unwrapping a proxy
36
+ * Determine the ultimate target class of the given spring bean instance, traversing
37
+ * not only a top-level spring proxy but any number of nested spring proxies as well —
38
+ * as long as possible without side effects, that is, just for singleton targets.
39
+ * @param candidate the instance to check (might be a spring AOP proxy)
40
+ * @return the ultimate target class (or the plain class of the given
41
+ * object as fallback; never {@code null})
44
42
*/
45
- public static <T > T getSpringUltimateTargetObject (Object candidate ) {
46
- try {
47
- if (isSpringAopProxy (candidate ) && implementsInterface (candidate .getClass (), SPRING_ADVISED_CLASS_NAME )) {
48
- Object targetSource = MethodUtils .invokeMethod (candidate , "getTargetSource" );
49
- Object target = MethodUtils .invokeMethod (targetSource , "getTarget" );
50
- return getSpringUltimateTargetObject (target );
43
+ public static Class <?> springUltimateTargetClass (Object candidate ) {
44
+ Object current = candidate ;
45
+ Class <?> result = null ;
46
+ while (null != current && implementsInterface (current .getClass (), SPRING_TARGETCLASSAWARE_CLASS_NAME )) {
47
+ try {
48
+ result = (Class <?>) MethodUtils .invokeMethod (current , "getTargetClass" );
49
+ } catch (Throwable ignored ) {
51
50
}
51
+ current = getSingletonTarget (current );
52
52
}
53
- catch (Throwable ex ) {
54
- throw new IllegalStateException ("Failed to unwrap proxied object" , ex );
53
+ if (result == null ) {
54
+ Class <?> clazz = candidate .getClass ();
55
+ result = (isCglibProxyClass (clazz ) ? clazz .getSuperclass () : candidate .getClass ());
55
56
}
56
- return ( T ) candidate ;
57
+ return result ;
57
58
}
58
59
59
60
/**
@@ -66,6 +67,26 @@ public static boolean isSpringAopProxy(Object object) {
66
67
|| isCglibProxyClass (clazz )));
67
68
}
68
69
70
+ /**
71
+ * Obtain the singleton target object behind the given spring proxy, if any.
72
+ * @param candidate the (potential) spring proxy to check
73
+ * @return the singleton target object, or {@code null} in any other case
74
+ * (not a spring proxy, not an existing singleton target)
75
+ */
76
+ private static Object getSingletonTarget (Object candidate ) {
77
+ try {
78
+ if (implementsInterface (candidate .getClass (), SPRING_ADVISED_CLASS_NAME )) {
79
+ Object targetSource = MethodUtils .invokeMethod (candidate , "getTargetSource" );
80
+ if (implementsInterface (targetSource .getClass (), SPRING_SINGLETONTARGETSOURCE_CLASS_NAME )) {
81
+ return MethodUtils .invokeMethod (targetSource , "getTarget" );
82
+ }
83
+ }
84
+ } catch (Throwable ignored ) {
85
+ }
86
+
87
+ return null ;
88
+ }
89
+
69
90
/**
70
91
* Check whether the specified class is a CGLIB-generated class.
71
92
* @param clazz the class to check
@@ -81,7 +102,7 @@ private static boolean isCglibProxyClass(Class<?> clazz) {
81
102
*/
82
103
private static boolean implementsInterface (Class <?> clazz , String ifaceClassName ) {
83
104
try {
84
- Class ifaceClass = ClassLoaderUtil .loadClass (ifaceClassName , ProxyUtil .class );
105
+ Class <?> ifaceClass = ClassLoaderUtil .loadClass (ifaceClassName , ProxyUtil .class );
85
106
return ifaceClass .isAssignableFrom (clazz );
86
107
} catch (ClassNotFoundException e ) {
87
108
return false ;
0 commit comments