1 16 17 package org.springframework.aop.aspectj; 18 19 import java.lang.reflect.InvocationTargetException ; 20 import java.lang.reflect.Method ; 21 import java.lang.reflect.Modifier ; 22 import java.util.HashMap ; 23 import java.util.Map ; 24 25 import org.aopalliance.aop.Advice; 26 import org.aopalliance.intercept.MethodInvocation; 27 import org.aspectj.lang.JoinPoint; 28 import org.aspectj.lang.ProceedingJoinPoint; 29 import org.aspectj.weaver.tools.JoinPointMatch; 30 import org.aspectj.weaver.tools.PointcutParameter; 31 32 import org.springframework.aop.AopInvocationException; 33 import org.springframework.aop.MethodMatcher; 34 import org.springframework.aop.Pointcut; 35 import org.springframework.aop.ProxyMethodInvocation; 36 import org.springframework.aop.interceptor.ExposeInvocationInterceptor; 37 import org.springframework.aop.support.ComposablePointcut; 38 import org.springframework.aop.support.MethodMatchers; 39 import org.springframework.aop.support.StaticMethodMatcher; 40 import org.springframework.beans.factory.InitializingBean; 41 import org.springframework.core.LocalVariableTableParameterNameDiscoverer; 42 import org.springframework.core.ParameterNameDiscoverer; 43 import org.springframework.core.PrioritizedParameterNameDiscoverer; 44 import org.springframework.util.Assert; 45 import org.springframework.util.ClassUtils; 46 import org.springframework.util.StringUtils; 47 48 57 public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation, InitializingBean { 58 59 62 protected final static String JOIN_POINT_KEY = JoinPoint.class.getName(); 63 64 65 73 public static JoinPoint currentJoinPoint() { 74 MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation(); 75 if (!(mi instanceof ProxyMethodInvocation)) { 76 throw new IllegalStateException ("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); 77 } 78 ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; 79 JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY); 80 if (jp == null) { 81 jp = new MethodInvocationProceedingJoinPoint(pmi); 82 pmi.setUserAttribute(JOIN_POINT_KEY, jp); 83 } 84 return jp; 85 } 86 87 88 protected final Method aspectJAdviceMethod; 89 90 91 private final int adviceInvocationArgumentCount; 92 93 private final AspectJExpressionPointcut pointcut; 94 95 private final AspectInstanceFactory aspectInstanceFactory; 96 97 102 private String aspectName; 103 104 107 private int declarationOrder; 108 109 113 private String [] argumentNames = null; 114 115 116 private String throwingName = null; 117 118 119 private String returningName = null; 120 121 private Class discoveredReturningType = Object .class; 122 123 private Class discoveredThrowingType = Object .class; 124 125 129 private int joinPointArgumentIndex = -1; 130 131 135 private int joinPointStaticPartArgumentIndex = -1; 136 137 private final Map argumentBindings = new HashMap (); 138 139 140 146 public AbstractAspectJAdvice( 147 Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aspectInstanceFactory) { 148 149 Assert.notNull(aspectJAdviceMethod, "Advice method must not be null"); 150 this.aspectJAdviceMethod = aspectJAdviceMethod; 151 this.adviceInvocationArgumentCount = this.aspectJAdviceMethod.getParameterTypes().length; 152 this.pointcut = pointcut; 153 this.aspectInstanceFactory = aspectInstanceFactory; 154 } 155 156 157 160 public final Method getAspectJAdviceMethod() { 161 return this.aspectJAdviceMethod; 162 } 163 164 167 public final AspectJExpressionPointcut getPointcut() { 168 return this.pointcut; 169 } 170 171 176 public final Pointcut buildSafePointcut() { 177 MethodMatcher safeMethodMatcher = MethodMatchers.intersection( 178 new AdviceExcludingMethodMatcher(this.aspectJAdviceMethod), this.pointcut.getMethodMatcher()); 179 return new ComposablePointcut(this.pointcut.getClassFilter(), safeMethodMatcher); 180 } 181 182 185 public final AspectInstanceFactory getAspectInstanceFactory() { 186 return this.aspectInstanceFactory; 187 } 188 189 public int getOrder() { 190 return this.aspectInstanceFactory.getOrder(); 191 } 192 193 194 public void setAspectName(String name) { 195 this.aspectName = name; 196 } 197 198 public String getAspectName() { 199 return this.aspectName; 200 } 201 202 205 public void setDeclarationOrder(int order) { 206 this.declarationOrder = order; 207 } 208 209 public int getDeclarationOrder() { 210 return this.declarationOrder; 211 } 212 213 219 public void setArgumentNames(String argNames) { 220 String [] tokens = StringUtils.commaDelimitedListToStringArray(argNames); 221 setArgumentNamesFromStringArray(tokens); 222 } 223 224 public void setArgumentNamesFromStringArray(String [] args) { 225 this.argumentNames = new String [args.length]; 226 for (int i = 0; i < args.length; i++) { 227 this.argumentNames[i] = StringUtils.trimWhitespace(args[i]); 228 if (!isVariableName(this.argumentNames[i])) { 229 throw new IllegalArgumentException ( 230 "'argumentNames' property of AbstractAspectJAdvice contains an argument name '" + 231 this.argumentNames[i] + "' that is not a valid Java identifier"); 232 } 233 } 234 } 235 236 public void setReturningName(String name) { 237 throw new UnsupportedOperationException ("Only afterReturning advice can be used to bind a return value"); 238 } 239 240 244 protected void setReturningNameNoCheck(String name) { 245 if (isVariableName(name)) { 247 this.returningName = name; 248 } 249 else { 250 try { 252 this.discoveredReturningType = ClassUtils.forName(name); 253 } 254 catch (Throwable ex) { 255 throw new IllegalArgumentException ("Returning name '" + name + 256 "' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. " + 257 "Root cause: " + ex); 258 } 259 } 260 } 261 262 protected Class getDiscoveredReturningType() { 263 return this.discoveredReturningType; 264 } 265 266 public void setThrowingName(String name) { 267 throw new UnsupportedOperationException ("Only afterThrowing advice can be used to bind a thrown exception"); 268 } 269 270 274 protected void setThrowingNameNoCheck(String name) { 275 if (isVariableName(name)) { 277 this.throwingName = name; 278 } 279 else { 280 try { 282 this.discoveredThrowingType = ClassUtils.forName(name); 283 } 284 catch (Throwable ex) { 285 throw new IllegalArgumentException ("Throwing name '" + name + 286 "' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. " + 287 "Root cause: " + ex); 288 } 289 } 290 } 291 292 protected Class getDiscoveredThrowingType() { 293 return this.discoveredThrowingType; 294 } 295 296 private boolean isVariableName(String name) { 297 char[] chars = name.toCharArray(); 298 if (!Character.isJavaIdentifierStart(chars[0])) { 299 return false; 300 } 301 for (int i = 1; i < chars.length; i++) { 302 if (!Character.isJavaIdentifierPart(chars[i])) { 303 return false; 304 } 305 } 306 return true; 307 } 308 309 310 315 public void afterPropertiesSet() throws Exception { 316 calculateArgumentBindings(); 317 } 318 319 332 private void calculateArgumentBindings() { 333 if (this.adviceInvocationArgumentCount == 0) { 335 return; 336 } 337 338 int numUnboundArgs = this.adviceInvocationArgumentCount; 339 340 Class [] parameterTypes = this.aspectJAdviceMethod.getParameterTypes(); 341 if (maybeBindJoinPoint(parameterTypes[0])) { 342 numUnboundArgs--; 343 } 344 else if (maybeBindJoinPointStaticPart(parameterTypes[0])) { 345 numUnboundArgs--; 346 } 347 348 if (numUnboundArgs > 0) { 349 bindArgumentsByName(numUnboundArgs); 351 } 352 } 353 354 private boolean maybeBindJoinPoint(Class candidateParameterType) { 355 if ((candidateParameterType.equals(JoinPoint.class)) || 356 (candidateParameterType.equals(ProceedingJoinPoint.class))) { 357 this.joinPointArgumentIndex = 0; 358 return true; 359 } 360 else { 361 return false; 362 } 363 } 364 365 private boolean maybeBindJoinPointStaticPart(Class candidateParameterType) { 366 if (candidateParameterType.equals(JoinPoint.StaticPart.class)) { 367 this.joinPointStaticPartArgumentIndex = 0; 368 return true; 369 } 370 else { 371 return false; 372 } 373 } 374 375 private void bindArgumentsByName(int numArgumentsExpectingToBind) { 376 if (this.argumentNames == null) { 377 this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod); 378 } 379 if (this.argumentNames != null) { 380 bindExplicitArguments(numArgumentsExpectingToBind); 382 } 383 else { 384 throw new IllegalStateException ("Advice method [" + this.aspectJAdviceMethod.getName() + "] " + 385 "requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " + 386 "the argument names were not specified and could not be discovered."); 387 } 388 } 389 390 396 protected ParameterNameDiscoverer createParameterNameDiscoverer() { 397 PrioritizedParameterNameDiscoverer discoverer = new PrioritizedParameterNameDiscoverer(); 400 discoverer.addDiscoverer(new LocalVariableTableParameterNameDiscoverer()); 401 AspectJAdviceParameterNameDiscoverer adviceParameterNameDiscoverer = 402 new AspectJAdviceParameterNameDiscoverer(this.pointcut.getExpression()); 403 adviceParameterNameDiscoverer.setReturningName(this.returningName); 404 adviceParameterNameDiscoverer.setThrowingName(this.throwingName); 405 adviceParameterNameDiscoverer.setRaiseExceptions(true); 407 discoverer.addDiscoverer(adviceParameterNameDiscoverer); 408 return discoverer; 409 } 410 411 private void bindExplicitArguments(int numArgumentsLeftToBind) { 412 int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterTypes().length; 413 if (this.argumentNames.length != numExpectedArgumentNames) { 414 throw new IllegalStateException ("Expecting to find " + numExpectedArgumentNames 415 + " arguments to bind by name in advice, but actually found " + 416 this.argumentNames.length + " arguments."); 417 } 418 419 int argumentIndexOffset = this.adviceInvocationArgumentCount - numArgumentsLeftToBind; 421 for (int i = argumentIndexOffset; i < this.argumentNames.length; i++) { 422 this.argumentBindings.put(this.argumentNames[i],new Integer (i)); 423 } 424 425 if (this.returningName != null) { 428 if (!this.argumentBindings.containsKey(this.returningName)) { 429 throw new IllegalStateException ("Returning argument name '" 430 + this.returningName + "' was not bound in advice arguments"); 431 } 432 else { 433 Integer index = (Integer ) this.argumentBindings.get(this.returningName); 434 this.discoveredReturningType = this.aspectJAdviceMethod.getParameterTypes()[index.intValue()]; 435 } 436 } 437 if (this.throwingName != null) { 438 if (!this.argumentBindings.containsKey(this.throwingName)) { 439 throw new IllegalStateException ("Throwing argument name '" 440 + this.throwingName + "' was not bound in advice arguments"); 441 } 442 else { 443 Integer index = (Integer ) this.argumentBindings.get(this.throwingName); 444 this.discoveredThrowingType = this.aspectJAdviceMethod.getParameterTypes()[index.intValue()]; 445 } 446 } 447 448 configurePointcutParameters(argumentIndexOffset); 450 } 451 452 457 private void configurePointcutParameters(int argumentIndexOffset) { 458 int numParametersToRemove = argumentIndexOffset; 459 if (returningName != null) { 460 numParametersToRemove++; 461 } 462 if (throwingName != null) { 463 numParametersToRemove++; 464 } 465 String [] pointcutParameterNames = new String [this.argumentNames.length - numParametersToRemove]; 466 Class [] pointcutParameterTypes = new Class [pointcutParameterNames.length]; 467 Class [] methodParameterTypes = this.aspectJAdviceMethod.getParameterTypes(); 468 469 int index = 0; 470 for (int i = 0; i < this.argumentNames.length; i++) { 471 if (i < argumentIndexOffset) { 472 continue; 473 } 474 if (this.argumentNames[i].equals(this.returningName) || 475 this.argumentNames[i].equals(this.throwingName)) { 476 continue; 477 } 478 pointcutParameterNames[index] = this.argumentNames[i]; 479 pointcutParameterTypes[index] = methodParameterTypes[i]; 480 index++; 481 } 482 483 this.pointcut.setParameterNames(pointcutParameterNames); 484 this.pointcut.setParameterTypes(pointcutParameterTypes); 485 } 486 487 495 protected Object [] argBinding(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t) { 496 Object [] adviceInvocationArgs = new Object [this.adviceInvocationArgumentCount]; 498 int numBound = 0; 499 500 if (this.joinPointArgumentIndex != -1) { 501 adviceInvocationArgs[this.joinPointArgumentIndex] = jp; 502 numBound++; 503 } 504 else if (this.joinPointStaticPartArgumentIndex != -1) { 505 adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart(); 506 numBound++; 507 } 508 509 if (!this.argumentBindings.isEmpty()) { 510 if (jpMatch != null) { 512 PointcutParameter[] parameterBindings = jpMatch.getParameterBindings(); 513 for (int i = 0; i < parameterBindings.length; i++) { 514 PointcutParameter parameter = parameterBindings[i]; 515 String name = parameter.getName(); 516 Integer index = (Integer ) this.argumentBindings.get(name); 517 adviceInvocationArgs[index.intValue()] = parameter.getBinding(); 518 numBound++; 519 } 520 } 521 if (this.returningName != null) { 523 Integer index = (Integer ) this.argumentBindings.get(this.returningName); 524 adviceInvocationArgs[index.intValue()] = returnValue; 525 numBound++; 526 } 527 if (this.throwingName != null) { 529 Integer index = (Integer ) this.argumentBindings.get(this.throwingName); 530 adviceInvocationArgs[index.intValue()] = t; 531 numBound++; 532 } 533 } 534 535 if (numBound != this.adviceInvocationArgumentCount) { 536 throw new IllegalStateException ("Required to bind " + this.adviceInvocationArgumentCount 537 + " arguments, but only bound " + numBound + " (JoinPointMatch " + 538 (jpMatch == null ? "was NOT" : "WAS") + 539 " bound in invocation)"); 540 } 541 542 return adviceInvocationArgs; 543 } 544 545 546 554 protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable { 555 return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex)); 556 } 557 558 protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t) 560 throws Throwable { 561 562 return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t)); 563 } 564 565 protected Object invokeAdviceMethodWithGivenArgs(Object [] args) throws Throwable { 566 Object [] actualArgs = args; 567 if (this.aspectJAdviceMethod.getParameterTypes().length == 0) { 568 actualArgs = null; 569 } 570 try { 571 if (!Modifier.isPublic(this.aspectJAdviceMethod.getModifiers()) || 572 !Modifier.isPublic(this.aspectJAdviceMethod.getDeclaringClass().getModifiers())) { 573 this.aspectJAdviceMethod.setAccessible(true); 574 } 575 return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs); 577 } 578 catch (IllegalArgumentException ex) { 579 throw new AopInvocationException("Mismatch on arguments to advice method [" + 580 this.aspectJAdviceMethod + "]; pointcut expression [" + 581 this.pointcut.getPointcutExpression() + "]", ex); 582 } 583 catch (InvocationTargetException ex) { 584 throw ex.getTargetException(); 585 } 586 } 587 588 591 protected JoinPoint getJoinPoint() { 592 return currentJoinPoint(); 593 } 594 595 598 protected JoinPointMatch getJoinPointMatch() { 599 MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation(); 600 if (!(mi instanceof ProxyMethodInvocation)) { 601 throw new IllegalStateException ("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); 602 } 603 return getJoinPointMatch((ProxyMethodInvocation) mi); 604 } 605 606 protected JoinPointMatch getJoinPointMatch(ProxyMethodInvocation rmi) { 613 return (JoinPointMatch) rmi.getUserAttribute(this.pointcut.getExpression()); 614 } 615 616 617 public String toString() { 618 return getClass().getName() + ": advice method [" + this.aspectJAdviceMethod + "]; " + 619 "aspect name '" + this.aspectName + "'"; 620 } 621 622 623 627 private static class AdviceExcludingMethodMatcher extends StaticMethodMatcher { 628 629 private final Method adviceMethod; 630 631 public AdviceExcludingMethodMatcher(Method adviceMethod) { 632 this.adviceMethod = adviceMethod; 633 } 634 635 public boolean matches(Method method, Class targetClass) { 636 return !this.adviceMethod.equals(method); 637 } 638 } 639 640 } 641 | Popular Tags |