1 16 17 package org.springframework.aop.aspectj; 18 19 import java.lang.reflect.Method ; 20 import java.util.HashMap ; 21 import java.util.HashSet ; 22 import java.util.Map ; 23 import java.util.Set ; 24 25 import org.aopalliance.intercept.MethodInvocation; 26 import org.apache.commons.logging.Log; 27 import org.apache.commons.logging.LogFactory; 28 import org.aspectj.weaver.BCException; 29 import org.aspectj.weaver.reflect.ReflectionWorld; 30 import org.aspectj.weaver.tools.JoinPointMatch; 31 import org.aspectj.weaver.tools.PointcutExpression; 32 import org.aspectj.weaver.tools.PointcutParameter; 33 import org.aspectj.weaver.tools.PointcutParser; 34 import org.aspectj.weaver.tools.PointcutPrimitive; 35 import org.aspectj.weaver.tools.ShadowMatch; 36 37 import org.springframework.aop.ClassFilter; 38 import org.springframework.aop.IntroductionAwareMethodMatcher; 39 import org.springframework.aop.MethodMatcher; 40 import org.springframework.aop.ProxyMethodInvocation; 41 import org.springframework.aop.interceptor.ExposeInvocationInterceptor; 42 import org.springframework.aop.support.AbstractExpressionPointcut; 43 import org.springframework.aop.support.AopUtils; 44 import org.springframework.util.ObjectUtils; 45 import org.springframework.util.StringUtils; 46 47 63 public class AspectJExpressionPointcut extends AbstractExpressionPointcut 64 implements ClassFilter, IntroductionAwareMethodMatcher { 65 66 private static final Set DEFAULT_SUPPORTED_PRIMITIVES = new HashSet (); 67 68 static { 69 DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION); 70 DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS); 71 DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE); 72 DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS); 73 DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET); 74 DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN); 75 DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION); 76 DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN); 77 DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS); 78 DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET); 79 } 80 81 82 private static final Log logger = LogFactory.getLog(AspectJExpressionPointcut.class); 83 84 private final Map shadowMapCache = new HashMap (); 85 86 private PointcutParser pointcutParser; 87 88 private Class pointcutDeclarationScope; 89 90 private String [] pointcutParameterNames = new String [0]; 91 92 private Class [] pointcutParameterTypes = new Class [0]; 93 94 private PointcutExpression pointcutExpression; 95 96 97 100 public AspectJExpressionPointcut() { 101 this(DEFAULT_SUPPORTED_PRIMITIVES); 102 } 103 104 109 public AspectJExpressionPointcut(Set supportedPrimitives) { 110 this.pointcutParser = 111 PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution( 112 supportedPrimitives); 113 } 114 115 121 public AspectJExpressionPointcut(Class declarationScope, String [] paramNames, Class [] paramTypes) { 122 this(DEFAULT_SUPPORTED_PRIMITIVES); 123 this.pointcutDeclarationScope = declarationScope; 124 if (paramNames.length != paramTypes.length) { 125 throw new IllegalStateException ( 126 "Number of pointcut parameter names must match number of pointcut parameter types"); 127 } 128 this.pointcutParameterNames = paramNames; 129 this.pointcutParameterTypes = paramTypes; 130 } 131 132 133 136 public void setPointcutDeclarationScope(Class pointcutDeclarationScope) { 137 this.pointcutDeclarationScope = pointcutDeclarationScope; 138 } 139 140 143 public void setParameterNames(String [] names) { 144 this.pointcutParameterNames = names; 145 } 146 147 150 public void setParameterTypes(Class [] types) { 151 this.pointcutParameterTypes = types; 152 } 153 154 155 public ClassFilter getClassFilter() { 156 checkReadyToMatch(); 157 return this; 158 } 159 160 public MethodMatcher getMethodMatcher() { 161 checkReadyToMatch(); 162 return this; 163 } 164 165 166 170 private void checkReadyToMatch() { 171 if (getExpression() == null) { 172 throw new IllegalStateException ("Must set property 'expression' before attempting to match"); 173 } 174 if (this.pointcutExpression == null) { 175 this.pointcutExpression = buildPointcutExpression(); 176 } 177 } 178 179 182 private PointcutExpression buildPointcutExpression() { 183 PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length]; 184 for (int i = 0; i < pointcutParameters.length; i++) { 185 pointcutParameters[i] = this.pointcutParser.createPointcutParameter( 186 this.pointcutParameterNames[i], this.pointcutParameterTypes[i]); 187 } 188 return this.pointcutParser.parsePointcutExpression( 189 replaceBooleanOperators(getExpression()), this.pointcutDeclarationScope, pointcutParameters); 190 } 191 192 198 private String replaceBooleanOperators(String pcExpr) { 199 pcExpr = StringUtils.replace(pcExpr," and "," && "); 200 pcExpr = StringUtils.replace(pcExpr, " or ", " || "); 201 pcExpr = StringUtils.replace(pcExpr, " not ", " ! "); 202 return pcExpr; 203 } 204 205 208 public PointcutExpression getPointcutExpression() { 209 checkReadyToMatch(); 210 return this.pointcutExpression; 211 } 212 213 214 public boolean matches(Class targetClass) { 215 checkReadyToMatch(); 216 try { 217 return this.pointcutExpression.couldMatchJoinPointsInType(targetClass); 218 } 219 catch (BCException ex) { 220 logger.debug("PointcutExpression matching rejected target class", ex); 221 return false; 222 } 223 } 224 225 public boolean matches(Method method, Class targetClass, boolean beanHasIntroductions) { 226 checkReadyToMatch(); 227 Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass); 228 ShadowMatch shadowMatch = null; 229 try { 230 shadowMatch = getShadowMatch(targetMethod, method); 231 } 232 catch (ReflectionWorld.ReflectionWorldException ex) { 233 return false; 236 } 237 238 if (shadowMatch.alwaysMatches()) { 242 return true; 243 } 244 else if (shadowMatch.neverMatches()) { 245 return false; 246 } 247 else { 248 return (beanHasIntroductions || matchesIgnoringSubtypes(shadowMatch)); 250 } 251 } 252 253 public boolean matches(Method method, Class targetClass) { 254 return matches(method, targetClass, false); 255 } 256 257 public boolean isRuntime() { 258 checkReadyToMatch(); 259 return this.pointcutExpression.mayNeedDynamicTest(); 260 } 261 262 public boolean matches(Method method, Class targetClass, Object [] args) { 263 checkReadyToMatch(); 264 Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass); 265 ShadowMatch shadowMatch = null; 266 try { 267 shadowMatch = getShadowMatch(targetMethod, method); 268 } 269 catch (ReflectionWorld.ReflectionWorldException ex) { 270 return false; 273 } 274 275 ProxyMethodInvocation pmi = null; 278 Object targetObject = null; 279 Object thisObject = null; 280 try { 281 MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation(); 282 targetObject = mi.getThis(); 283 if (!(mi instanceof ProxyMethodInvocation)) { 284 throw new IllegalStateException ("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); 285 } 286 pmi = (ProxyMethodInvocation) mi; 287 thisObject = pmi.getProxy(); 288 } 289 catch (IllegalStateException ex) { 290 logger.debug("Couldn't access current invocation - matching with limited context: " + ex); 293 } 294 295 JoinPointMatch joinPointMatch = shadowMatch.matchesJoinPoint(thisObject, targetObject, args); 296 if (joinPointMatch.matches() && pmi != null) { 297 bindParameters(pmi, joinPointMatch); 298 } 299 return joinPointMatch.matches(); 300 } 301 302 303 309 private boolean matchesIgnoringSubtypes(ShadowMatch shadowMatch) { 310 return !(new RuntimeTestWalker(shadowMatch).testsSubtypeSensitiveVars()); 311 } 312 313 private void bindParameters(ProxyMethodInvocation invocation, JoinPointMatch jpm) { 314 invocation.setUserAttribute(getExpression(), jpm); 321 } 322 323 private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) { 324 synchronized (this.shadowMapCache) { 325 ShadowMatch shadowMatch = (ShadowMatch) this.shadowMapCache.get(targetMethod); 326 if (shadowMatch == null) { 327 try { 328 shadowMatch = this.pointcutExpression.matchesMethodExecution(targetMethod); 329 } 330 catch (ReflectionWorld.ReflectionWorldException ex) { 331 if (targetMethod == originalMethod) { 334 throw ex; 335 } 336 shadowMatch = this.pointcutExpression.matchesMethodExecution(originalMethod); 337 } 338 this.shadowMapCache.put(targetMethod, shadowMatch); 339 } 340 return shadowMatch; 341 } 342 } 343 344 345 public boolean equals(Object other) { 346 if (this == other) { 347 return true; 348 } 349 if (!(other instanceof AspectJExpressionPointcut)) { 350 return false; 351 } 352 AspectJExpressionPointcut otherPc = (AspectJExpressionPointcut) other; 353 return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) && 354 ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) && 355 ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) && 356 ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, otherPc.pointcutParameterTypes); 357 } 358 359 public int hashCode() { 360 int hashCode = ObjectUtils.nullSafeHashCode(this.getExpression()); 361 hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutDeclarationScope); 362 hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterNames); 363 hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterTypes); 364 return hashCode; 365 } 366 367 public String toString() { 368 StringBuffer sb = new StringBuffer (); 369 sb.append("AspectJExpressionPointcut: "); 370 if (this.pointcutParameterNames != null && this.pointcutParameterTypes != null) { 371 sb.append("("); 372 for (int i = 0; i < this.pointcutParameterTypes.length; i++) { 373 sb.append(this.pointcutParameterTypes[i].getName()); 374 sb.append(" "); 375 sb.append(this.pointcutParameterNames[i]); 376 if ((i+1) < this.pointcutParameterTypes.length) { 377 sb.append(", "); 378 } 379 } 380 sb.append(")"); 381 } 382 sb.append(" "); 383 if (getExpression() != null) { 384 sb.append(getExpression()); 385 } 386 else { 387 sb.append("<pointcut expression not set>"); 388 } 389 return sb.toString(); 390 } 391 392 } 393 | Popular Tags |