1 /*
2  * Copyright 2002-2010 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.LinkedList;
24 import java.util.List;
25 import java.util.Set;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28 
29 import org.springframework.aop.Advisor;
30 import org.springframework.aop.AopInvocationException;
31 import org.springframework.aop.IntroductionAdvisor;
32 import org.springframework.aop.IntroductionAwareMethodMatcher;
33 import org.springframework.aop.MethodMatcher;
34 import org.springframework.aop.Pointcut;
35 import org.springframework.aop.PointcutAdvisor;
36 import org.springframework.aop.SpringProxy;
37 import org.springframework.aop.TargetClassAware;
38 import org.springframework.core.BridgeMethodResolver;
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 	 */
isAopProxy(Object object)64 	public static boolean isAopProxy(Object object) {
65 		return (object instanceof SpringProxy &&
66 				(Proxy.isProxyClass(object.getClass()) || ClassUtils.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 	 */
isJdkDynamicProxy(Object object)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. Goes beyond the implementation
80 	 * in {@link ClassUtils#isCglibProxy(Object)} by checking also to see if the given
81 	 * object is an instance of {@link SpringProxy}.
82 	 * @param object the object to check
83 	 * @see ClassUtils#isCglibProxy(Object)
84 	 */
isCglibProxy(Object object)85 	public static boolean isCglibProxy(Object object) {
86 		return (object instanceof SpringProxy && ClassUtils.isCglibProxy(object));
87 	}
88 
89 	/**
90 	 * Check whether the specified class is a CGLIB-generated class.
91 	 * @param clazz the class to check
92 	 * @deprecated as of Spring 3.1 in favor of {@link ClassUtils#isCglibProxyClass(Class)}
93 	 */
94 	@Deprecated
isCglibProxyClass(Class<?> clazz)95 	public static boolean isCglibProxyClass(Class<?> clazz) {
96 		return ClassUtils.isCglibProxyClass(clazz);
97 	}
98 
99 	/**
100 	 * Check whether the specified class name is a CGLIB-generated class.
101 	 * @param className the class name to check
102 	 * @deprecated as of Spring 3.1 in favor of {@link ClassUtils#isCglibProxyClassName(String)}
103 	 */
104 	@Deprecated
isCglibProxyClassName(String className)105 	public static boolean isCglibProxyClassName(String className) {
106 		return ClassUtils.isCglibProxyClassName(className);
107 	}
108 
109 	/**
110 	 * Determine the target class of the given bean instance which might be an AOP proxy.
111 	 * <p>Returns the target class for an AOP proxy and the plain class else.
112 	 * @param candidate the instance to check (might be an AOP proxy)
113 	 * @return the target class (or the plain class of the given object as fallback;
114 	 * never <code>null</code>)
115 	 * @see org.springframework.aop.TargetClassAware#getTargetClass()
116 	 * @see org.springframework.aop.framework.AopProxyUtils#ultimateTargetClass(Object)
117 	 */
getTargetClass(Object candidate)118 	public static Class<?> getTargetClass(Object candidate) {
119 		Assert.notNull(candidate, "Candidate object must not be null");
120 		Class<?> result = null;
121 		if (candidate instanceof TargetClassAware) {
122 			result = ((TargetClassAware) candidate).getTargetClass();
123 		}
124 		if (result == null) {
125 			result = (isCglibProxy(candidate) ? candidate.getClass().getSuperclass() : candidate.getClass());
126 		}
127 		return result;
128 	}
129 
130 	/**
131 	 * Determine whether the given method is an "equals" method.
132 	 * @see java.lang.Object#equals
133 	 */
isEqualsMethod(Method method)134 	public static boolean isEqualsMethod(Method method) {
135 		return ReflectionUtils.isEqualsMethod(method);
136 	}
137 
138 	/**
139 	 * Determine whether the given method is a "hashCode" method.
140 	 * @see java.lang.Object#hashCode
141 	 */
isHashCodeMethod(Method method)142 	public static boolean isHashCodeMethod(Method method) {
143 		return ReflectionUtils.isHashCodeMethod(method);
144 	}
145 
146 	/**
147 	 * Determine whether the given method is a "toString" method.
148 	 * @see java.lang.Object#toString()
149 	 */
isToStringMethod(Method method)150 	public static boolean isToStringMethod(Method method) {
151 		return ReflectionUtils.isToStringMethod(method);
152 	}
153 
154 	/**
155 	 * Determine whether the given method is a "finalize" method.
156 	 * @see java.lang.Object#finalize()
157 	 */
isFinalizeMethod(Method method)158 	public static boolean isFinalizeMethod(Method method) {
159 		return (method != null && method.getName().equals("finalize") &&
160 				method.getParameterTypes().length == 0);
161 	}
162 
163 	/**
164 	 * Given a method, which may come from an interface, and a target class used
165 	 * in the current AOP invocation, find the corresponding target method if there
166 	 * is one. E.g. the method may be <code>IFoo.bar()</code> and the target class
167 	 * may be <code>DefaultFoo</code>. In this case, the method may be
168 	 * <code>DefaultFoo.bar()</code>. This enables attributes on that method to be found.
169 	 * <p><b>NOTE:</b> In contrast to {@link org.springframework.util.ClassUtils#getMostSpecificMethod},
170 	 * this method resolves Java 5 bridge methods in order to retrieve attributes
171 	 * from the <i>original</i> method definition.
172 	 * @param method the method to be invoked, which may come from an interface
173 	 * @param targetClass the target class for the current invocation.
174 	 * May be <code>null</code> or may not even implement the method.
175 	 * @return the specific target method, or the original method if the
176 	 * <code>targetClass</code> doesn't implement it or is <code>null</code>
177 	 * @see org.springframework.util.ClassUtils#getMostSpecificMethod
178 	 */
getMostSpecificMethod(Method method, Class<?> targetClass)179 	public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {
180 		Method resolvedMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
181 		// If we are dealing with method with generic parameters, find the original method.
182 		return BridgeMethodResolver.findBridgedMethod(resolvedMethod);
183 	}
184 
185 
186 	/**
187 	 * Can the given pointcut apply at all on the given class?
188 	 * <p>This is an important test as it can be used to optimize
189 	 * out a pointcut for a class.
190 	 * @param pc the static or dynamic pointcut to check
191 	 * @param targetClass the class to test
192 	 * @return whether the pointcut can apply on any method
193 	 */
canApply(Pointcut pc, Class<?> targetClass)194 	public static boolean canApply(Pointcut pc, Class<?> targetClass) {
195 		return canApply(pc, targetClass, false);
196 	}
197 
198 	/**
199 	 * Can the given pointcut apply at all on the given class?
200 	 * <p>This is an important test as it can be used to optimize
201 	 * out a pointcut for a class.
202 	 * @param pc the static or dynamic pointcut to check
203 	 * @param targetClass the class to test
204 	 * @param hasIntroductions whether or not the advisor chain
205 	 * for this bean includes any introductions
206 	 * @return whether the pointcut can apply on any method
207 	 */
canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions)208 	public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
209 		Assert.notNull(pc, "Pointcut must not be null");
210 		if (!pc.getClassFilter().matches(targetClass)) {
211 			return false;
212 		}
213 
214 		MethodMatcher methodMatcher = pc.getMethodMatcher();
215 		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
216 		if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
217 			introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
218 		}
219 
220 		Set<Class> classes = new HashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
221 		classes.add(targetClass);
222 		for (Class<?> clazz : classes) {
223 			Method[] methods = clazz.getMethods();
224 			for (Method method : methods) {
225 				if ((introductionAwareMethodMatcher != null &&
226 						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
227 						methodMatcher.matches(method, targetClass)) {
228 					return true;
229 				}
230 			}
231 		}
232 
233 		return false;
234 	}
235 
236 	/**
237 	 * Can the given advisor apply at all on the given class?
238 	 * This is an important test as it can be used to optimize
239 	 * out a advisor for a class.
240 	 * @param advisor the advisor to check
241 	 * @param targetClass class we're testing
242 	 * @return whether the pointcut can apply on any method
243 	 */
canApply(Advisor advisor, Class<?> targetClass)244 	public static boolean canApply(Advisor advisor, Class<?> targetClass) {
245 		return canApply(advisor, targetClass, false);
246 	}
247 
248 	/**
249 	 * Can the given advisor apply at all on the given class?
250 	 * <p>This is an important test as it can be used to optimize out a advisor for a class.
251 	 * This version also takes into account introductions (for IntroductionAwareMethodMatchers).
252 	 * @param advisor the advisor to check
253 	 * @param targetClass class we're testing
254 	 * @param hasIntroductions whether or not the advisor chain for this bean includes
255 	 * any introductions
256 	 * @return whether the pointcut can apply on any method
257 	 */
canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions)258 	public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
259 		if (advisor instanceof IntroductionAdvisor) {
260 			return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
261 		}
262 		else if (advisor instanceof PointcutAdvisor) {
263 			PointcutAdvisor pca = (PointcutAdvisor) advisor;
264 			return canApply(pca.getPointcut(), targetClass, hasIntroductions);
265 		}
266 		else {
267 			// It doesn't have a pointcut so we assume it applies.
268 			return true;
269 		}
270 	}
271 
272 	/**
273 	 * Determine the sublist of the <code>candidateAdvisors</code> list
274 	 * that is applicable to the given class.
275 	 * @param candidateAdvisors the Advisors to evaluate
276 	 * @param clazz the target class
277 	 * @return sublist of Advisors that can apply to an object of the given class
278 	 * (may be the incoming List as-is)
279 	 */
findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz)280 	public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
281 		if (candidateAdvisors.isEmpty()) {
282 			return candidateAdvisors;
283 		}
284 		List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
285 		for (Advisor candidate : candidateAdvisors) {
286 			if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
287 				eligibleAdvisors.add(candidate);
288 			}
289 		}
290 		boolean hasIntroductions = !eligibleAdvisors.isEmpty();
291 		for (Advisor candidate : candidateAdvisors) {
292 			if (candidate instanceof IntroductionAdvisor) {
293 				// already processed
294 				continue;
295 			}
296 			if (canApply(candidate, clazz, hasIntroductions)) {
297 				eligibleAdvisors.add(candidate);
298 			}
299 		}
300 		return eligibleAdvisors;
301 	}
302 
303 
304 	/**
305 	 * Invoke the given target via reflection, as part of an AOP method invocation.
306 	 * @param target the target object
307 	 * @param method the method to invoke
308 	 * @param args the arguments for the method
309 	 * @return the invocation result, if any
310 	 * @throws Throwable if thrown by the target method
311 	 * @throws org.springframework.aop.AopInvocationException in case of a reflection error
312 	 */
invokeJoinpointUsingReflection(Object target, Method method, Object[] args)313 	public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
314 			throws Throwable {
315 
316 		// Use reflection to invoke the method.
317 		try {
318 			ReflectionUtils.makeAccessible(method);
319 			return method.invoke(target, args);
320 		}
321 		catch (InvocationTargetException ex) {
322 			// Invoked method threw a checked exception.
323 			// We must rethrow it. The client won't see the interceptor.
324 			throw ex.getTargetException();
325 		}
326 		catch (IllegalArgumentException ex) {
327 			throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
328 					method + "] on target [" + target + "]", ex);
329 		}
330 		catch (IllegalAccessException ex) {
331 			throw new AopInvocationException("Could not access method [" + method + "]", ex);
332 		}
333 	}
334 
335 }
336