1 /* 2 * Copyright 2002-2012 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.aspectj; 18 19 import java.lang.reflect.InvocationTargetException; 20 import java.lang.reflect.Method; 21 import java.lang.reflect.Type; 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.core.LocalVariableTableParameterNameDiscoverer; 41 import org.springframework.core.ParameterNameDiscoverer; 42 import org.springframework.core.PrioritizedParameterNameDiscoverer; 43 import org.springframework.util.Assert; 44 import org.springframework.util.ClassUtils; 45 import org.springframework.util.CollectionUtils; 46 import org.springframework.util.ReflectionUtils; 47 import org.springframework.util.StringUtils; 48 49 /** 50 * Base class for AOP Alliance {@link org.aopalliance.aop.Advice} classes 51 * wrapping an AspectJ aspect or an AspectJ-annotated advice method. 52 * 53 * @author Rod Johnson 54 * @author Adrian Colyer 55 * @author Juergen Hoeller 56 * @author Ramnivas Laddad 57 * @since 2.0 58 */ 59 public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation { 60 61 /** 62 * Key used in ReflectiveMethodInvocation userAtributes map for the current joinpoint. 63 */ 64 protected static final String JOIN_POINT_KEY = JoinPoint.class.getName(); 65 66 67 /** 68 * Lazily instantiate joinpoint for the current invocation. 69 * Requires MethodInvocation to be bound with ExposeInvocationInterceptor. 70 * <p>Do not use if access is available to the current ReflectiveMethodInvocation 71 * (in an around advice). 72 * @return current AspectJ joinpoint, or through an exception if we're not in a 73 * Spring AOP invocation. 74 */ currentJoinPoint()75 public static JoinPoint currentJoinPoint() { 76 MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation(); 77 if (!(mi instanceof ProxyMethodInvocation)) { 78 throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); 79 } 80 ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; 81 JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY); 82 if (jp == null) { 83 jp = new MethodInvocationProceedingJoinPoint(pmi); 84 pmi.setUserAttribute(JOIN_POINT_KEY, jp); 85 } 86 return jp; 87 } 88 89 90 protected final Method aspectJAdviceMethod; 91 92 /** The total number of arguments we have to populate on advice dispatch */ 93 private final int adviceInvocationArgumentCount; 94 95 private final AspectJExpressionPointcut pointcut; 96 97 private final AspectInstanceFactory aspectInstanceFactory; 98 99 /** 100 * The name of the aspect (ref bean) in which this advice was defined (used 101 * when determining advice precedence so that we can determine 102 * whether two pieces of advice come from the same aspect). 103 */ 104 private String aspectName; 105 106 /** 107 * The order of declaration of this advice within the aspect. 108 */ 109 private int declarationOrder; 110 111 /** 112 * This will be non-null if the creator of this advice object knows the argument names 113 * and sets them explicitly 114 */ 115 private String[] argumentNames = null; 116 117 /** Non-null if after throwing advice binds the thrown value */ 118 private String throwingName = null; 119 120 /** Non-null if after returning advice binds the return value */ 121 private String returningName = null; 122 123 private Class discoveredReturningType = Object.class; 124 125 private Class discoveredThrowingType = Object.class; 126 127 /** 128 * Index for thisJoinPoint argument (currently only 129 * supported at index 0 if present at all) 130 */ 131 private int joinPointArgumentIndex = -1; 132 133 /** 134 * Index for thisJoinPointStaticPart argument (currently only 135 * supported at index 0 if present at all) 136 */ 137 private int joinPointStaticPartArgumentIndex = -1; 138 139 private Map<String, Integer> argumentBindings = null; 140 141 private boolean argumentsIntrospected = false; 142 143 private Type discoveredReturningGenericType; 144 // Note: Unlike return type, no such generic information is needed for the throwing type, 145 // since Java doesn't allow exception types to be parameterized. 146 147 148 /** 149 * Create a new AbstractAspectJAdvice for the given advice method. 150 * @param aspectJAdviceMethod the AspectJ-style advice method 151 * @param pointcut the AspectJ expression pointcut 152 * @param aspectInstanceFactory the factory for aspect instances 153 */ AbstractAspectJAdvice( Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aspectInstanceFactory)154 public AbstractAspectJAdvice( 155 Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aspectInstanceFactory) { 156 157 Assert.notNull(aspectJAdviceMethod, "Advice method must not be null"); 158 this.aspectJAdviceMethod = aspectJAdviceMethod; 159 this.adviceInvocationArgumentCount = this.aspectJAdviceMethod.getParameterTypes().length; 160 this.pointcut = pointcut; 161 this.aspectInstanceFactory = aspectInstanceFactory; 162 } 163 164 165 /** 166 * Return the AspectJ-style advice method. 167 */ getAspectJAdviceMethod()168 public final Method getAspectJAdviceMethod() { 169 return this.aspectJAdviceMethod; 170 } 171 172 /** 173 * Return the AspectJ expression pointcut. 174 */ getPointcut()175 public final AspectJExpressionPointcut getPointcut() { 176 calculateArgumentBindings(); 177 return this.pointcut; 178 } 179 180 /** 181 * Build a 'safe' pointcut that excludes the AspectJ advice method itself. 182 * @return a composable pointcut that builds on the original AspectJ expression pointcut 183 * @see #getPointcut() 184 */ buildSafePointcut()185 public final Pointcut buildSafePointcut() { 186 Pointcut pc = getPointcut(); 187 MethodMatcher safeMethodMatcher = MethodMatchers.intersection( 188 new AdviceExcludingMethodMatcher(this.aspectJAdviceMethod), pc.getMethodMatcher()); 189 return new ComposablePointcut(pc.getClassFilter(), safeMethodMatcher); 190 } 191 192 /** 193 * Return the factory for aspect instances. 194 */ getAspectInstanceFactory()195 public final AspectInstanceFactory getAspectInstanceFactory() { 196 return this.aspectInstanceFactory; 197 } 198 199 /** 200 * Return the ClassLoader for aspect instances. 201 */ getAspectClassLoader()202 public final ClassLoader getAspectClassLoader() { 203 return this.aspectInstanceFactory.getAspectClassLoader(); 204 } 205 getOrder()206 public int getOrder() { 207 return this.aspectInstanceFactory.getOrder(); 208 } 209 210 setAspectName(String name)211 public void setAspectName(String name) { 212 this.aspectName = name; 213 } 214 getAspectName()215 public String getAspectName() { 216 return this.aspectName; 217 } 218 219 /** 220 * Sets the <b>declaration order</b> of this advice within the aspect 221 */ setDeclarationOrder(int order)222 public void setDeclarationOrder(int order) { 223 this.declarationOrder = order; 224 } 225 getDeclarationOrder()226 public int getDeclarationOrder() { 227 return this.declarationOrder; 228 } 229 230 /** 231 * Set by creator of this advice object if the argument names are known. 232 * <p>This could be for example because they have been explicitly specified in XML, 233 * or in an advice annotation. 234 * @param argNames comma delimited list of arg names 235 */ setArgumentNames(String argNames)236 public void setArgumentNames(String argNames) { 237 String[] tokens = StringUtils.commaDelimitedListToStringArray(argNames); 238 setArgumentNamesFromStringArray(tokens); 239 } 240 setArgumentNamesFromStringArray(String[] args)241 public void setArgumentNamesFromStringArray(String[] args) { 242 this.argumentNames = new String[args.length]; 243 for (int i = 0; i < args.length; i++) { 244 this.argumentNames[i] = StringUtils.trimWhitespace(args[i]); 245 if (!isVariableName(this.argumentNames[i])) { 246 throw new IllegalArgumentException( 247 "'argumentNames' property of AbstractAspectJAdvice contains an argument name '" + 248 this.argumentNames[i] + "' that is not a valid Java identifier"); 249 } 250 } 251 if (argumentNames != null) { 252 if (aspectJAdviceMethod.getParameterTypes().length == argumentNames.length + 1) { 253 // May need to add implicit join point arg name... 254 Class firstArgType = aspectJAdviceMethod.getParameterTypes()[0]; 255 if (firstArgType == JoinPoint.class || 256 firstArgType == ProceedingJoinPoint.class || 257 firstArgType == JoinPoint.StaticPart.class) { 258 String[] oldNames = argumentNames; 259 argumentNames = new String[oldNames.length + 1]; 260 argumentNames[0] = "THIS_JOIN_POINT"; 261 System.arraycopy(oldNames, 0, argumentNames, 1, oldNames.length); 262 } 263 } 264 } 265 } 266 setReturningName(String name)267 public void setReturningName(String name) { 268 throw new UnsupportedOperationException("Only afterReturning advice can be used to bind a return value"); 269 } 270 271 /** 272 * We need to hold the returning name at this level for argument binding calculations, 273 * this method allows the afterReturning advice subclass to set the name. 274 */ setReturningNameNoCheck(String name)275 protected void setReturningNameNoCheck(String name) { 276 // name could be a variable or a type... 277 if (isVariableName(name)) { 278 this.returningName = name; 279 } 280 else { 281 // assume a type 282 try { 283 this.discoveredReturningType = ClassUtils.forName(name, getAspectClassLoader()); 284 } 285 catch (Throwable ex) { 286 throw new IllegalArgumentException("Returning name '" + name + 287 "' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. " + 288 "Root cause: " + ex); 289 } 290 } 291 } 292 getDiscoveredReturningType()293 protected Class getDiscoveredReturningType() { 294 return this.discoveredReturningType; 295 } 296 getDiscoveredReturningGenericType()297 protected Type getDiscoveredReturningGenericType() { 298 return this.discoveredReturningGenericType; 299 } 300 setThrowingName(String name)301 public void setThrowingName(String name) { 302 throw new UnsupportedOperationException("Only afterThrowing advice can be used to bind a thrown exception"); 303 } 304 305 /** 306 * We need to hold the throwing name at this level for argument binding calculations, 307 * this method allows the afterThrowing advice subclass to set the name. 308 */ setThrowingNameNoCheck(String name)309 protected void setThrowingNameNoCheck(String name) { 310 // name could be a variable or a type... 311 if (isVariableName(name)) { 312 this.throwingName = name; 313 } 314 else { 315 // assume a type 316 try { 317 this.discoveredThrowingType = ClassUtils.forName(name, getAspectClassLoader()); 318 } 319 catch (Throwable ex) { 320 throw new IllegalArgumentException("Throwing name '" + name + 321 "' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. " + 322 "Root cause: " + ex); 323 } 324 } 325 } 326 getDiscoveredThrowingType()327 protected Class getDiscoveredThrowingType() { 328 return this.discoveredThrowingType; 329 } 330 isVariableName(String name)331 private boolean isVariableName(String name) { 332 char[] chars = name.toCharArray(); 333 if (!Character.isJavaIdentifierStart(chars[0])) { 334 return false; 335 } 336 for (int i = 1; i < chars.length; i++) { 337 if (!Character.isJavaIdentifierPart(chars[i])) { 338 return false; 339 } 340 } 341 return true; 342 } 343 344 345 /** 346 * Do as much work as we can as part of the set-up so that argument binding 347 * on subsequent advice invocations can be as fast as possible. 348 * <p>If the first argument is of type JoinPoint or ProceedingJoinPoint then we 349 * pass a JoinPoint in that position (ProceedingJoinPoint for around advice). 350 * <p>If the first argument is of type <code>JoinPoint.StaticPart</code> 351 * then we pass a <code>JoinPoint.StaticPart</code> in that position. 352 * <p>Remaining arguments have to be bound by pointcut evaluation at 353 * a given join point. We will get back a map from argument name to 354 * value. We need to calculate which advice parameter needs to be bound 355 * to which argument name. There are multiple strategies for determining 356 * this binding, which are arranged in a ChainOfResponsibility. 357 */ calculateArgumentBindings()358 public synchronized final void calculateArgumentBindings() { 359 // The simple case... nothing to bind. 360 if (this.argumentsIntrospected || this.adviceInvocationArgumentCount == 0) { 361 return; 362 } 363 364 int numUnboundArgs = this.adviceInvocationArgumentCount; 365 Class[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes(); 366 if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0])) { 367 numUnboundArgs--; 368 } 369 else if (maybeBindJoinPointStaticPart(parameterTypes[0])) { 370 numUnboundArgs--; 371 } 372 373 if (numUnboundArgs > 0) { 374 // need to bind arguments by name as returned from the pointcut match 375 bindArgumentsByName(numUnboundArgs); 376 } 377 378 this.argumentsIntrospected = true; 379 } 380 maybeBindJoinPoint(Class candidateParameterType)381 private boolean maybeBindJoinPoint(Class candidateParameterType) { 382 if (candidateParameterType.equals(JoinPoint.class)) { 383 this.joinPointArgumentIndex = 0; 384 return true; 385 } 386 else { 387 return false; 388 } 389 } 390 maybeBindProceedingJoinPoint(Class candidateParameterType)391 private boolean maybeBindProceedingJoinPoint(Class candidateParameterType) { 392 if (candidateParameterType.equals(ProceedingJoinPoint.class)) { 393 if (!supportsProceedingJoinPoint()) { 394 throw new IllegalArgumentException("ProceedingJoinPoint is only supported for around advice"); 395 } 396 this.joinPointArgumentIndex = 0; 397 return true; 398 } 399 else { 400 return false; 401 } 402 } 403 supportsProceedingJoinPoint()404 protected boolean supportsProceedingJoinPoint() { 405 return false; 406 } 407 maybeBindJoinPointStaticPart(Class candidateParameterType)408 private boolean maybeBindJoinPointStaticPart(Class candidateParameterType) { 409 if (candidateParameterType.equals(JoinPoint.StaticPart.class)) { 410 this.joinPointStaticPartArgumentIndex = 0; 411 return true; 412 } 413 else { 414 return false; 415 } 416 } 417 bindArgumentsByName(int numArgumentsExpectingToBind)418 private void bindArgumentsByName(int numArgumentsExpectingToBind) { 419 if (this.argumentNames == null) { 420 this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod); 421 } 422 if (this.argumentNames != null) { 423 // We have been able to determine the arg names. 424 bindExplicitArguments(numArgumentsExpectingToBind); 425 } 426 else { 427 throw new IllegalStateException("Advice method [" + this.aspectJAdviceMethod.getName() + "] " + 428 "requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " + 429 "the argument names were not specified and could not be discovered."); 430 } 431 } 432 433 /** 434 * Create a ParameterNameDiscoverer to be used for argument binding. 435 * <p>The default implementation creates a {@link PrioritizedParameterNameDiscoverer} 436 * containing a {@link LocalVariableTableParameterNameDiscoverer} and an 437 * {@link AspectJAdviceParameterNameDiscoverer}. 438 */ createParameterNameDiscoverer()439 protected ParameterNameDiscoverer createParameterNameDiscoverer() { 440 // We need to discover them, or if that fails, guess, 441 // and if we can't guess with 100% accuracy, fail. 442 PrioritizedParameterNameDiscoverer discoverer = new PrioritizedParameterNameDiscoverer(); 443 discoverer.addDiscoverer(new LocalVariableTableParameterNameDiscoverer()); 444 AspectJAdviceParameterNameDiscoverer adviceParameterNameDiscoverer = 445 new AspectJAdviceParameterNameDiscoverer(this.pointcut.getExpression()); 446 adviceParameterNameDiscoverer.setReturningName(this.returningName); 447 adviceParameterNameDiscoverer.setThrowingName(this.throwingName); 448 // Last in chain, so if we're called and we fail, that's bad... 449 adviceParameterNameDiscoverer.setRaiseExceptions(true); 450 discoverer.addDiscoverer(adviceParameterNameDiscoverer); 451 return discoverer; 452 } 453 bindExplicitArguments(int numArgumentsLeftToBind)454 private void bindExplicitArguments(int numArgumentsLeftToBind) { 455 this.argumentBindings = new HashMap<String, Integer>(); 456 457 int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterTypes().length; 458 if (this.argumentNames.length != numExpectedArgumentNames) { 459 throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames 460 + " arguments to bind by name in advice, but actually found " + 461 this.argumentNames.length + " arguments."); 462 } 463 464 // So we match in number... 465 int argumentIndexOffset = this.adviceInvocationArgumentCount - numArgumentsLeftToBind; 466 for (int i = argumentIndexOffset; i < this.argumentNames.length; i++) { 467 this.argumentBindings.put(this.argumentNames[i], i); 468 } 469 470 // Check that returning and throwing were in the argument names list if 471 // specified, and find the discovered argument types. 472 if (this.returningName != null) { 473 if (!this.argumentBindings.containsKey(this.returningName)) { 474 throw new IllegalStateException("Returning argument name '" 475 + this.returningName + "' was not bound in advice arguments"); 476 } 477 else { 478 Integer index = this.argumentBindings.get(this.returningName); 479 this.discoveredReturningType = this.aspectJAdviceMethod.getParameterTypes()[index]; 480 this.discoveredReturningGenericType = this.aspectJAdviceMethod.getGenericParameterTypes()[index]; 481 } 482 } 483 if (this.throwingName != null) { 484 if (!this.argumentBindings.containsKey(this.throwingName)) { 485 throw new IllegalStateException("Throwing argument name '" 486 + this.throwingName + "' was not bound in advice arguments"); 487 } 488 else { 489 Integer index = this.argumentBindings.get(this.throwingName); 490 this.discoveredThrowingType = this.aspectJAdviceMethod.getParameterTypes()[index]; 491 } 492 } 493 494 // configure the pointcut expression accordingly. 495 configurePointcutParameters(argumentIndexOffset); 496 } 497 498 /** 499 * All parameters from argumentIndexOffset onwards are candidates for 500 * pointcut parameters - but returning and throwing vars are handled differently 501 * and must be removed from the list if present. 502 */ configurePointcutParameters(int argumentIndexOffset)503 private void configurePointcutParameters(int argumentIndexOffset) { 504 int numParametersToRemove = argumentIndexOffset; 505 if (this.returningName != null) { 506 numParametersToRemove++; 507 } 508 if (this.throwingName != null) { 509 numParametersToRemove++; 510 } 511 String[] pointcutParameterNames = new String[this.argumentNames.length - numParametersToRemove]; 512 Class[] pointcutParameterTypes = new Class[pointcutParameterNames.length]; 513 Class[] methodParameterTypes = this.aspectJAdviceMethod.getParameterTypes(); 514 515 int index = 0; 516 for (int i = 0; i < this.argumentNames.length; i++) { 517 if (i < argumentIndexOffset) { 518 continue; 519 } 520 if (this.argumentNames[i].equals(this.returningName) || 521 this.argumentNames[i].equals(this.throwingName)) { 522 continue; 523 } 524 pointcutParameterNames[index] = this.argumentNames[i]; 525 pointcutParameterTypes[index] = methodParameterTypes[i]; 526 index++; 527 } 528 529 this.pointcut.setParameterNames(pointcutParameterNames); 530 this.pointcut.setParameterTypes(pointcutParameterTypes); 531 } 532 533 /** 534 * Take the arguments at the method execution join point and output a set of arguments 535 * to the advice method 536 * @param jp the current JoinPoint 537 * @param jpMatch the join point match that matched this execution join point 538 * @param returnValue the return value from the method execution (may be null) 539 * @param ex the exception thrown by the method execution (may be null) 540 * @return the empty array if there are no arguments 541 */ argBinding(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable ex)542 protected Object[] argBinding(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable ex) { 543 calculateArgumentBindings(); 544 545 // AMC start 546 Object[] adviceInvocationArgs = new Object[this.adviceInvocationArgumentCount]; 547 int numBound = 0; 548 549 if (this.joinPointArgumentIndex != -1) { 550 adviceInvocationArgs[this.joinPointArgumentIndex] = jp; 551 numBound++; 552 } 553 else if (this.joinPointStaticPartArgumentIndex != -1) { 554 adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart(); 555 numBound++; 556 } 557 558 if (!CollectionUtils.isEmpty(this.argumentBindings)) { 559 // binding from pointcut match 560 if (jpMatch != null) { 561 PointcutParameter[] parameterBindings = jpMatch.getParameterBindings(); 562 for (PointcutParameter parameter : parameterBindings) { 563 String name = parameter.getName(); 564 Integer index = this.argumentBindings.get(name); 565 adviceInvocationArgs[index] = parameter.getBinding(); 566 numBound++; 567 } 568 } 569 // binding from returning clause 570 if (this.returningName != null) { 571 Integer index = this.argumentBindings.get(this.returningName); 572 adviceInvocationArgs[index] = returnValue; 573 numBound++; 574 } 575 // binding from thrown exception 576 if (this.throwingName != null) { 577 Integer index = this.argumentBindings.get(this.throwingName); 578 adviceInvocationArgs[index] = ex; 579 numBound++; 580 } 581 } 582 583 if (numBound != this.adviceInvocationArgumentCount) { 584 throw new IllegalStateException("Required to bind " + this.adviceInvocationArgumentCount 585 + " arguments, but only bound " + numBound + " (JoinPointMatch " + 586 (jpMatch == null ? "was NOT" : "WAS") + 587 " bound in invocation)"); 588 } 589 590 return adviceInvocationArgs; 591 } 592 593 594 /** 595 * Invoke the advice method. 596 * @param jpMatch the JoinPointMatch that matched this execution join point 597 * @param returnValue the return value from the method execution (may be null) 598 * @param ex the exception thrown by the method execution (may be null) 599 * @return the invocation result 600 * @throws Throwable in case of invocation failure 601 */ invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex)602 protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable { 603 return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex)); 604 } 605 606 // As above, but in this case we are given the join point. invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t)607 protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t) 608 throws Throwable { 609 610 return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t)); 611 } 612 invokeAdviceMethodWithGivenArgs(Object[] args)613 protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable { 614 Object[] actualArgs = args; 615 if (this.aspectJAdviceMethod.getParameterTypes().length == 0) { 616 actualArgs = null; 617 } 618 try { 619 ReflectionUtils.makeAccessible(this.aspectJAdviceMethod); 620 // TODO AopUtils.invokeJoinpointUsingReflection 621 return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs); 622 } 623 catch (IllegalArgumentException ex) { 624 throw new AopInvocationException("Mismatch on arguments to advice method [" + 625 this.aspectJAdviceMethod + "]; pointcut expression [" + 626 this.pointcut.getPointcutExpression() + "]", ex); 627 } 628 catch (InvocationTargetException ex) { 629 throw ex.getTargetException(); 630 } 631 } 632 633 /** 634 * Overridden in around advice to return proceeding join point. 635 */ getJoinPoint()636 protected JoinPoint getJoinPoint() { 637 return currentJoinPoint(); 638 } 639 640 /** 641 * Get the current join point match at the join point we are being dispatched on. 642 */ getJoinPointMatch()643 protected JoinPointMatch getJoinPointMatch() { 644 MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation(); 645 if (!(mi instanceof ProxyMethodInvocation)) { 646 throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi); 647 } 648 return getJoinPointMatch((ProxyMethodInvocation) mi); 649 } 650 651 // Note: We can't use JoinPointMatch.getClass().getName() as the key, since 652 // Spring AOP does all the matching at a join point, and then all the invocations. 653 // Under this scenario, if we just use JoinPointMatch as the key, then 654 // 'last man wins' which is not what we want at all. 655 // Using the expression is guaranteed to be safe, since 2 identical expressions 656 // are guaranteed to bind in exactly the same way. getJoinPointMatch(ProxyMethodInvocation pmi)657 protected JoinPointMatch getJoinPointMatch(ProxyMethodInvocation pmi) { 658 return (JoinPointMatch) pmi.getUserAttribute(this.pointcut.getExpression()); 659 } 660 661 662 @Override toString()663 public String toString() { 664 return getClass().getName() + ": advice method [" + this.aspectJAdviceMethod + "]; " + 665 "aspect name '" + this.aspectName + "'"; 666 } 667 668 669 /** 670 * MethodMatcher that excludes the specified advice method. 671 * @see AbstractAspectJAdvice#buildSafePointcut() 672 */ 673 private static class AdviceExcludingMethodMatcher extends StaticMethodMatcher { 674 675 private final Method adviceMethod; 676 AdviceExcludingMethodMatcher(Method adviceMethod)677 public AdviceExcludingMethodMatcher(Method adviceMethod) { 678 this.adviceMethod = adviceMethod; 679 } 680 matches(Method method, Class targetClass)681 public boolean matches(Method method, Class targetClass) { 682 return !this.adviceMethod.equals(method); 683 } 684 685 @Override equals(Object other)686 public boolean equals(Object other) { 687 if (this == other) { 688 return true; 689 } 690 if (!(other instanceof AdviceExcludingMethodMatcher)) { 691 return false; 692 } 693 AdviceExcludingMethodMatcher otherMm = (AdviceExcludingMethodMatcher) other; 694 return this.adviceMethod.equals(otherMm.adviceMethod); 695 } 696 697 @Override hashCode()698 public int hashCode() { 699 return this.adviceMethod.hashCode(); 700 } 701 } 702 703 } 704