001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.aop.support;
018:
019: import java.lang.reflect.InvocationTargetException;
020: import java.lang.reflect.Method;
021: import java.lang.reflect.Proxy;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.LinkedList;
025: import java.util.List;
026: import java.util.Set;
027:
028: import org.springframework.aop.Advisor;
029: import org.springframework.aop.AopInvocationException;
030: import org.springframework.aop.IntroductionAdvisor;
031: import org.springframework.aop.IntroductionAwareMethodMatcher;
032: import org.springframework.aop.MethodMatcher;
033: import org.springframework.aop.Pointcut;
034: import org.springframework.aop.PointcutAdvisor;
035: import org.springframework.aop.SpringProxy;
036: import org.springframework.aop.TargetClassAware;
037: import org.springframework.core.BridgeMethodResolver;
038: import org.springframework.core.JdkVersion;
039: import org.springframework.util.Assert;
040: import org.springframework.util.ClassUtils;
041: import org.springframework.util.ReflectionUtils;
042:
043: /**
044: * Utility methods for AOP support code.
045: * Mainly for internal use within Spring's AOP support.
046: *
047: * <p>See {@link org.springframework.aop.framework.AopProxyUtils} for a
048: * collection of framework-specific AOP utility methods which depend
049: * on internals of Spring's AOP framework implementation.
050: *
051: * @author Rod Johnson
052: * @author Juergen Hoeller
053: * @author Rob Harrop
054: * @see org.springframework.aop.framework.AopProxyUtils
055: */
056: public abstract class AopUtils {
057:
058: /**
059: * Check whether the given object is a JDK dynamic proxy or a CGLIB proxy.
060: * @param object the object to check
061: * @see #isJdkDynamicProxy
062: * @see #isCglibProxy
063: */
064: public static boolean isAopProxy(Object object) {
065: return (object instanceof SpringProxy && (Proxy
066: .isProxyClass(object.getClass()) || isCglibProxyClass(object
067: .getClass())));
068: }
069:
070: /**
071: * Check whether the given object is a JDK dynamic proxy.
072: * @param object the object to check
073: * @see java.lang.reflect.Proxy#isProxyClass
074: */
075: public static boolean isJdkDynamicProxy(Object object) {
076: return (object instanceof SpringProxy && Proxy
077: .isProxyClass(object.getClass()));
078: }
079:
080: /**
081: * Check whether the given object is a CGLIB proxy.
082: * @param object the object to check
083: */
084: public static boolean isCglibProxy(Object object) {
085: return (object instanceof SpringProxy && isCglibProxyClass(object
086: .getClass()));
087: }
088:
089: /**
090: * Check whether the specified class is a CGLIB-generated class.
091: * @param clazz the class to check
092: */
093: public static boolean isCglibProxyClass(Class clazz) {
094: return (clazz != null && clazz.getName().indexOf(
095: ClassUtils.CGLIB_CLASS_SEPARATOR) != -1);
096: }
097:
098: /**
099: * Determine the target class of the given bean instance,
100: * which might be an AOP proxy.
101: * <p>Returns the target class for an AOP proxy and the plain class else.
102: * @param candidate the instance to check (might be an AOP proxy)
103: * @return the target class (or the plain class of the given object as fallback)
104: * @see org.springframework.aop.TargetClassAware#getTargetClass()
105: */
106: public static Class getTargetClass(Object candidate) {
107: Assert.notNull(candidate, "Candidate object must not be null");
108: if (candidate instanceof TargetClassAware) {
109: return ((TargetClassAware) candidate).getTargetClass();
110: }
111: if (isCglibProxyClass(candidate.getClass())) {
112: return candidate.getClass().getSuperclass();
113: }
114: return candidate.getClass();
115: }
116:
117: /**
118: * Determine whether the given method is an "equals" method.
119: * @see java.lang.Object#equals
120: */
121: public static boolean isEqualsMethod(Method method) {
122: return (method != null && method.getName().equals("equals")
123: && method.getParameterTypes().length == 1 && method
124: .getParameterTypes()[0] == Object.class);
125: }
126:
127: /**
128: * Determine whether the given method is a "hashCode" method.
129: * @see java.lang.Object#hashCode
130: */
131: public static boolean isHashCodeMethod(Method method) {
132: return (method != null && method.getName().equals("hashCode") && method
133: .getParameterTypes().length == 0);
134: }
135:
136: /**
137: * Determine whether the given method is a "toString" method.
138: * @see java.lang.Object#toString()
139: */
140: public static boolean isToStringMethod(Method method) {
141: return (method != null && method.getName().equals("toString") && method
142: .getParameterTypes().length == 0);
143: }
144:
145: /**
146: * Given a method, which may come from an interface, and a target class used
147: * in the current AOP invocation, find the corresponding target method if there
148: * is one. E.g. the method may be <code>IFoo.bar()</code> and the target class
149: * may be <code>DefaultFoo</code>. In this case, the method may be
150: * <code>DefaultFoo.bar()</code>. This enables attributes on that method to be found.
151: * <p><b>NOTE:</b> In contrast to {@link org.springframework.util.ClassUtils#getMostSpecificMethod},
152: * this method resolves Java 5 bridge methods in order to retrieve attributes
153: * from the <i>original</i> method definition.
154: * @param method the method to be invoked, which may come from an interface
155: * @param targetClass the target class for the current invocation.
156: * May be <code>null</code> or may not even implement the method.
157: * @return the specific target method, or the original method if the
158: * <code>targetClass</code> doesn't implement it or is <code>null</code>
159: * @see org.springframework.util.ClassUtils#getMostSpecificMethod
160: */
161: public static Method getMostSpecificMethod(Method method,
162: Class targetClass) {
163: Method resolvedMethod = ClassUtils.getMostSpecificMethod(
164: method, targetClass);
165: // If we are dealing with method with generic parameters, find the original method.
166: if (JdkVersion.isAtLeastJava15()) {
167: resolvedMethod = BridgeMethodResolver
168: .findBridgedMethod(resolvedMethod);
169: }
170: return resolvedMethod;
171: }
172:
173: /**
174: * Can the given pointcut apply at all on the given class?
175: * <p>This is an important test as it can be used to optimize
176: * out a pointcut for a class.
177: * @param pc the static or dynamic pointcut to check
178: * @param targetClass the class to test
179: * @return whether the pointcut can apply on any method
180: */
181: public static boolean canApply(Pointcut pc, Class targetClass) {
182: return canApply(pc, targetClass, false);
183: }
184:
185: /**
186: * Can the given pointcut apply at all on the given class?
187: * <p>This is an important test as it can be used to optimize
188: * out a pointcut for a class.
189: * @param pc the static or dynamic pointcut to check
190: * @param targetClass the class to test
191: * @param hasIntroductions whether or not the advisor chain
192: * for this bean includes any introductions
193: * @return whether the pointcut can apply on any method
194: */
195: public static boolean canApply(Pointcut pc, Class targetClass,
196: boolean hasIntroductions) {
197: if (!pc.getClassFilter().matches(targetClass)) {
198: return false;
199: }
200:
201: MethodMatcher methodMatcher = pc.getMethodMatcher();
202: IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
203: if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
204: introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
205: }
206:
207: Set classes = new HashSet(ClassUtils
208: .getAllInterfacesForClassAsSet(targetClass));
209: classes.add(targetClass);
210: for (Iterator it = classes.iterator(); it.hasNext();) {
211: Class clazz = (Class) it.next();
212: Method[] methods = clazz.getMethods();
213: for (int j = 0; j < methods.length; j++) {
214: if ((introductionAwareMethodMatcher != null && introductionAwareMethodMatcher
215: .matches(methods[j], targetClass,
216: hasIntroductions))
217: || methodMatcher.matches(methods[j],
218: targetClass)) {
219: return true;
220: }
221: }
222: }
223:
224: return false;
225: }
226:
227: /**
228: * Can the given advisor apply at all on the given class?
229: * This is an important test as it can be used to optimize
230: * out a advisor for a class.
231: * @param advisor the advisor to check
232: * @param targetClass class we're testing
233: * @return whether the pointcut can apply on any method
234: */
235: public static boolean canApply(Advisor advisor, Class targetClass) {
236: return canApply(advisor, targetClass, false);
237: }
238:
239: /**
240: * Can the given advisor apply at all on the given class?
241: * <p>This is an important test as it can be used to optimize out a advisor for a class.
242: * This version also takes into account introductions (for IntroductionAwareMethodMatchers).
243: * @param advisor the advisor to check
244: * @param targetClass class we're testing
245: * @param hasIntroductions whether or not the advisor chain for this bean includes
246: * any introductions
247: * @return whether the pointcut can apply on any method
248: */
249: public static boolean canApply(Advisor advisor, Class targetClass,
250: boolean hasIntroductions) {
251: if (advisor instanceof IntroductionAdvisor) {
252: return ((IntroductionAdvisor) advisor).getClassFilter()
253: .matches(targetClass);
254: } else if (advisor instanceof PointcutAdvisor) {
255: PointcutAdvisor pca = (PointcutAdvisor) advisor;
256: return canApply(pca.getPointcut(), targetClass,
257: hasIntroductions);
258: } else {
259: // It doesn't have a pointcut so we assume it applies.
260: return true;
261: }
262: }
263:
264: /**
265: * Determine the sublist of the <code>candidateAdvisors</code> list
266: * that is applicable to the given class.
267: * @param candidateAdvisors the Advisors to evaluate
268: * @param clazz the target class
269: * @return sublist of Advisors that can apply to an object of the given class
270: * (may be the incoming List as-is)
271: */
272: public static List findAdvisorsThatCanApply(List candidateAdvisors,
273: Class clazz) {
274: if (candidateAdvisors.isEmpty()) {
275: return candidateAdvisors;
276: }
277: List eligibleAdvisors = new LinkedList();
278: for (Iterator it = candidateAdvisors.iterator(); it.hasNext();) {
279: Advisor candidate = (Advisor) it.next();
280: if (candidate instanceof IntroductionAdvisor
281: && canApply(candidate, clazz)) {
282: eligibleAdvisors.add(candidate);
283: }
284: }
285: boolean hasIntroductions = !eligibleAdvisors.isEmpty();
286: for (Iterator it = candidateAdvisors.iterator(); it.hasNext();) {
287: Advisor candidate = (Advisor) it.next();
288: if (candidate instanceof IntroductionAdvisor) {
289: // already processed
290: continue;
291: }
292: if (canApply(candidate, clazz, hasIntroductions)) {
293: eligibleAdvisors.add(candidate);
294: }
295: }
296: return eligibleAdvisors;
297: }
298:
299: /**
300: * Invoke the given target via reflection, as part of an AOP method invocation.
301: * @param target the target object
302: * @param method the method to invoke
303: * @param args the arguments for the method
304: * @return the invocation result, if any
305: * @throws Throwable if thrown by the target method
306: * @throws org.springframework.aop.AopInvocationException in case of a reflection error
307: */
308: public static Object invokeJoinpointUsingReflection(Object target,
309: Method method, Object[] args) throws Throwable {
310:
311: // Use reflection to invoke the method.
312: try {
313: ReflectionUtils.makeAccessible(method);
314: return method.invoke(target, args);
315: } catch (InvocationTargetException ex) {
316: // Invoked method threw a checked exception.
317: // We must rethrow it. The client won't see the interceptor.
318: throw ex.getTargetException();
319: } catch (IllegalArgumentException ex) {
320: throw new AopInvocationException(
321: "AOP configuration seems to be invalid: tried calling method ["
322: + method + "] on target [" + target + "]",
323: ex);
324: } catch (IllegalAccessException ex) {
325: throw new AopInvocationException(
326: "Could not access method [" + method + "]", ex);
327: }
328: }
329:
330: }
|