1 /*
2 * Copyright 2002-2008 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.springframework.aop.support;
18
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Proxy;
22 import java.util.HashSet;
23 import java.util.Iterator;
24 import java.util.LinkedList;
25 import java.util.List;
26 import java.util.Set;
27
28 import org.springframework.aop.Advisor;
29 import org.springframework.aop.AopInvocationException;
30 import org.springframework.aop.IntroductionAdvisor;
31 import org.springframework.aop.IntroductionAwareMethodMatcher;
32 import org.springframework.aop.MethodMatcher;
33 import org.springframework.aop.Pointcut;
34 import org.springframework.aop.PointcutAdvisor;
35 import org.springframework.aop.SpringProxy;
36 import org.springframework.aop.TargetClassAware;
37 import org.springframework.core.BridgeMethodResolver;
38 import org.springframework.core.JdkVersion;
39 import org.springframework.util.Assert;
40 import org.springframework.util.ClassUtils;
41 import org.springframework.util.ReflectionUtils;
42
43 /**
44 * Utility methods for AOP support code.
45 * Mainly for internal use within Spring's AOP support.
46 *
47 * <p>See {@link org.springframework.aop.framework.AopProxyUtils} for a
48 * collection of framework-specific AOP utility methods which depend
49 * on internals of Spring's AOP framework implementation.
50 *
51 * @author Rod Johnson
52 * @author Juergen Hoeller
53 * @author Rob Harrop
54 * @see org.springframework.aop.framework.AopProxyUtils
55 */
56 public abstract class AopUtils {
57
58 /**
59 * Check whether the given object is a JDK dynamic proxy or a CGLIB proxy.
60 * @param object the object to check
61 * @see #isJdkDynamicProxy
62 * @see #isCglibProxy
63 */
64 public static boolean isAopProxy(Object object) {
65 return (object instanceof SpringProxy &&
66 (Proxy.isProxyClass(object.getClass()) || isCglibProxyClass(object.getClass())));
67 }
68
69 /**
70 * Check whether the given object is a JDK dynamic proxy.
71 * @param object the object to check
72 * @see java.lang.reflect.Proxy#isProxyClass
73 */
74 public static boolean isJdkDynamicProxy(Object object) {
75 return (object instanceof SpringProxy && Proxy.isProxyClass(object.getClass()));
76 }
77
78 /**
79 * Check whether the given object is a CGLIB proxy.
80 * @param object the object to check
81 */
82 public static boolean isCglibProxy(Object object) {
83 return (object instanceof SpringProxy && isCglibProxyClass(object.getClass()));
84 }
85
86 /**
87 * Check whether the specified class is a CGLIB-generated class.
88 * @param clazz the class to check
89 */
90 public static boolean isCglibProxyClass(Class clazz) {
91 return (clazz != null && clazz.getName().indexOf(ClassUtils.CGLIB_CLASS_SEPARATOR) != -1);
92 }
93
94 /**
95 * Determine the target class of the given bean instance,
96 * which might be an AOP proxy.
97 * <p>Returns the target class for an AOP proxy and the plain class else.
98 * @param candidate the instance to check (might be an AOP proxy)
99 * @return the target class (or the plain class of the given object as fallback)
100 * @see org.springframework.aop.TargetClassAware#getTargetClass()
101 */
102 public static Class getTargetClass(Object candidate) {
103 Assert.notNull(candidate, "Candidate object must not be null");
104 if (candidate instanceof TargetClassAware) {
105 return ((TargetClassAware) candidate).getTargetClass();
106 }
107 if (isCglibProxyClass(candidate.getClass())) {
108 return candidate.getClass().getSuperclass();
109 }
110 return candidate.getClass();
111 }
112
113 /**
114 * Determine whether the given method is an "equals" method.
115 * @see java.lang.Object#equals
116 */
117 public static boolean isEqualsMethod(Method method) {
118 return ReflectionUtils.isEqualsMethod(method);
119 }
120
121 /**
122 * Determine whether the given method is a "hashCode" method.
123 * @see java.lang.Object#hashCode
124 */
125 public static boolean isHashCodeMethod(Method method) {
126 return ReflectionUtils.isHashCodeMethod(method);
127 }
128
129 /**
130 * Determine whether the given method is a "toString" method.
131 * @see java.lang.Object#toString()
132 */
133 public static boolean isToStringMethod(Method method) {
134 return ReflectionUtils.isToStringMethod(method);
135 }
136
137 /**
138 * Determine whether the given method is a "finalize" method.
139 * @see java.lang.Object#finalize()
140 */
141 public static boolean isFinalizeMethod(Method method) {
142 return (method != null && method.getName().equals("finalize") &&
143 method.getParameterTypes().length == 0);
144 }
145
146 /**
147 * Given a method, which may come from an interface, and a target class used
148 * in the current AOP invocation, find the corresponding target method if there
149 * is one. E.g. the method may be <code>IFoo.bar()</code> and the target class
150 * may be <code>DefaultFoo</code>. In this case, the method may be
151 * <code>DefaultFoo.bar()</code>. This enables attributes on that method to be found.
152 * <p><b>NOTE:</b> In contrast to {@link org.springframework.util.ClassUtils#getMostSpecificMethod},
153 * this method resolves Java 5 bridge methods in order to retrieve attributes
154 * from the <i>original</i> method definition.
155 * @param method the method to be invoked, which may come from an interface
156 * @param targetClass the target class for the current invocation.
157 * May be <code>null</code> or may not even implement the method.
158 * @return the specific target method, or the original method if the
159 * <code>targetClass</code> doesn't implement it or is <code>null</code>
160 * @see org.springframework.util.ClassUtils#getMostSpecificMethod
161 */
162 public static Method getMostSpecificMethod(Method method, Class targetClass) {
163 Method resolvedMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
164 // If we are dealing with method with generic parameters, find the original method.
165 if (JdkVersion.isAtLeastJava15()) {
166 resolvedMethod = BridgeMethodResolver.findBridgedMethod(resolvedMethod);
167 }
168 return resolvedMethod;
169 }
170
171
172 /**
173 * Can the given pointcut apply at all on the given class?
174 * <p>This is an important test as it can be used to optimize
175 * out a pointcut for a class.
176 * @param pc the static or dynamic pointcut to check
177 * @param targetClass the class to test
178 * @return whether the pointcut can apply on any method
179 */
180 public static boolean canApply(Pointcut pc, Class targetClass) {
181 return canApply(pc, targetClass, false);
182 }
183
184 /**
185 * Can the given pointcut apply at all on the given class?
186 * <p>This is an important test as it can be used to optimize
187 * out a pointcut for a class.
188 * @param pc the static or dynamic pointcut to check
189 * @param targetClass the class to test
190 * @param hasIntroductions whether or not the advisor chain
191 * for this bean includes any introductions
192 * @return whether the pointcut can apply on any method
193 */
194 public static boolean canApply(Pointcut pc, Class targetClass, boolean hasIntroductions) {
195 if (!pc.getClassFilter().matches(targetClass)) {
196 return false;
197 }
198
199 MethodMatcher methodMatcher = pc.getMethodMatcher();
200 IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
201 if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
202 introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
203 }
204
205 Set classes = new HashSet(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
206 classes.add(targetClass);
207 for (Iterator it = classes.iterator(); it.hasNext();) {
208 Class clazz = (Class) it.next();
209 Method[] methods = clazz.getMethods();
210 for (int j = 0; j < methods.length; j++) {
211 if ((introductionAwareMethodMatcher != null &&
212 introductionAwareMethodMatcher.matches(methods[j], targetClass, hasIntroductions)) ||
213 methodMatcher.matches(methods[j], targetClass)) {
214 return true;
215 }
216 }
217 }
218
219 return false;
220 }
221
222 /**
223 * Can the given advisor apply at all on the given class?
224 * This is an important test as it can be used to optimize
225 * out a advisor for a class.
226 * @param advisor the advisor to check
227 * @param targetClass class we're testing
228 * @return whether the pointcut can apply on any method
229 */
230 public static boolean canApply(Advisor advisor, Class targetClass) {
231 return canApply(advisor, targetClass, false);
232 }
233
234 /**
235 * Can the given advisor apply at all on the given class?
236 * <p>This is an important test as it can be used to optimize out a advisor for a class.
237 * This version also takes into account introductions (for IntroductionAwareMethodMatchers).
238 * @param advisor the advisor to check
239 * @param targetClass class we're testing
240 * @param hasIntroductions whether or not the advisor chain for this bean includes
241 * any introductions
242 * @return whether the pointcut can apply on any method
243 */
244 public static boolean canApply(Advisor advisor, Class targetClass, boolean hasIntroductions) {
245 if (advisor instanceof IntroductionAdvisor) {
246 return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
247 }
248 else if (advisor instanceof PointcutAdvisor) {
249 PointcutAdvisor pca = (PointcutAdvisor) advisor;
250 return canApply(pca.getPointcut(), targetClass, hasIntroductions);
251 }
252 else {
253 // It doesn't have a pointcut so we assume it applies.
254 return true;
255 }
256 }
257
258 /**
259 * Determine the sublist of the <code>candidateAdvisors</code> list
260 * that is applicable to the given class.
261 * @param candidateAdvisors the Advisors to evaluate
262 * @param clazz the target class
263 * @return sublist of Advisors that can apply to an object of the given class
264 * (may be the incoming List as-is)
265 */
266 public static List findAdvisorsThatCanApply(List candidateAdvisors, Class clazz) {
267 if (candidateAdvisors.isEmpty()) {
268 return candidateAdvisors;
269 }
270 List eligibleAdvisors = new LinkedList();
271 for (Iterator it = candidateAdvisors.iterator(); it.hasNext();) {
272 Advisor candidate = (Advisor) it.next();
273 if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
274 eligibleAdvisors.add(candidate);
275 }
276 }
277 boolean hasIntroductions = !eligibleAdvisors.isEmpty();
278 for (Iterator it = candidateAdvisors.iterator(); it.hasNext();) {
279 Advisor candidate = (Advisor) it.next();
280 if (candidate instanceof IntroductionAdvisor) {
281 // already processed
282 continue;
283 }
284 if (canApply(candidate, clazz, hasIntroductions)) {
285 eligibleAdvisors.add(candidate);
286 }
287 }
288 return eligibleAdvisors;
289 }
290
291
292 /**
293 * Invoke the given target via reflection, as part of an AOP method invocation.
294 * @param target the target object
295 * @param method the method to invoke
296 * @param args the arguments for the method
297 * @return the invocation result, if any
298 * @throws Throwable if thrown by the target method
299 * @throws org.springframework.aop.AopInvocationException in case of a reflection error
300 */
301 public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
302 throws Throwable {
303
304 // Use reflection to invoke the method.
305 try {
306 ReflectionUtils.makeAccessible(method);
307 return method.invoke(target, args);
308 }
309 catch (InvocationTargetException ex) {
310 // Invoked method threw a checked exception.
311 // We must rethrow it. The client won't see the interceptor.
312 throw ex.getTargetException();
313 }
314 catch (IllegalArgumentException ex) {
315 throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
316 method + "] on target [" + target + "]", ex);
317 }
318 catch (IllegalAccessException ex) {
319 throw new AopInvocationException("Could not access method [" + method + "]", ex);
320 }
321 }
322
323 }