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