View Javadoc
1   /*
2    * Copyright 2002-2014 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.DefaultParameterNameDiscoverer;
41  import org.springframework.core.ParameterNameDiscoverer;
42  import org.springframework.util.Assert;
43  import org.springframework.util.ClassUtils;
44  import org.springframework.util.CollectionUtils;
45  import org.springframework.util.ReflectionUtils;
46  import org.springframework.util.StringUtils;
47  
48  /**
49   * Base class for AOP Alliance {@link org.aopalliance.aop.Advice} classes
50   * wrapping an AspectJ aspect or an AspectJ-annotated advice method.
51   *
52   * @author Rod Johnson
53   * @author Adrian Colyer
54   * @author Juergen Hoeller
55   * @author Ramnivas Laddad
56   * @since 2.0
57   */
58  public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedenceInformation {
59  
60  	/**
61  	 * Key used in ReflectiveMethodInvocation userAtributes map for the current joinpoint.
62  	 */
63  	protected static final String JOIN_POINT_KEY = JoinPoint.class.getName();
64  
65  
66  	/**
67  	 * Lazily instantiate joinpoint for the current invocation.
68  	 * Requires MethodInvocation to be bound with ExposeInvocationInterceptor.
69  	 * <p>Do not use if access is available to the current ReflectiveMethodInvocation
70  	 * (in an around advice).
71  	 * @return current AspectJ joinpoint, or through an exception if we're not in a
72  	 * Spring AOP invocation.
73  	 */
74  	public static JoinPoint currentJoinPoint() {
75  		MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
76  		if (!(mi instanceof ProxyMethodInvocation)) {
77  			throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
78  		}
79  		ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
80  		JoinPoint jp = (JoinPoint) pmi.getUserAttribute(JOIN_POINT_KEY);
81  		if (jp == null) {
82  			jp = new MethodInvocationProceedingJoinPoint(pmi);
83  			pmi.setUserAttribute(JOIN_POINT_KEY, jp);
84  		}
85  		return jp;
86  	}
87  
88  
89  	protected final Method aspectJAdviceMethod;
90  
91  	/** The total number of arguments we have to populate on advice dispatch */
92  	private final int adviceInvocationArgumentCount;
93  
94  	private final AspectJExpressionPointcut pointcut;
95  
96  	private final AspectInstanceFactory aspectInstanceFactory;
97  
98  	/**
99  	 * The name of the aspect (ref bean) in which this advice was defined (used
100 	 * when determining advice precedence so that we can determine
101 	 * whether two pieces of advice come from the same aspect).
102 	 */
103 	private String aspectName;
104 
105 	/**
106 	 * The order of declaration of this advice within the aspect.
107 	 */
108 	private int declarationOrder;
109 
110 	/**
111 	 * This will be non-null if the creator of this advice object knows the argument names
112 	 * and sets them explicitly
113 	 */
114 	private String[] argumentNames = null;
115 
116 	/** Non-null if after throwing advice binds the thrown value */
117 	private String throwingName = null;
118 
119 	/** Non-null if after returning advice binds the return value */
120 	private String returningName = null;
121 
122 	private Class<?> discoveredReturningType = Object.class;
123 
124 	private Class<?> discoveredThrowingType = Object.class;
125 
126 	/**
127 	 * Index for thisJoinPoint argument (currently only
128 	 * supported at index 0 if present at all)
129 	 */
130 	private int joinPointArgumentIndex = -1;
131 
132 	/**
133 	 * Index for thisJoinPointStaticPart argument (currently only
134 	 * supported at index 0 if present at all)
135 	 */
136 	private int joinPointStaticPartArgumentIndex = -1;
137 
138 	private Map<String, Integer> argumentBindings = null;
139 
140 	private boolean argumentsIntrospected = false;
141 
142 	private Type discoveredReturningGenericType;
143 	// Note: Unlike return type, no such generic information is needed for the throwing type,
144 	// since Java doesn't allow exception types to be parameterized.
145 
146 
147 	/**
148 	 * Create a new AbstractAspectJAdvice for the given advice method.
149 	 * @param aspectJAdviceMethod the AspectJ-style advice method
150 	 * @param pointcut the AspectJ expression pointcut
151 	 * @param aspectInstanceFactory the factory for aspect instances
152 	 */
153 	public AbstractAspectJAdvice(
154 			Method aspectJAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aspectInstanceFactory) {
155 
156 		Assert.notNull(aspectJAdviceMethod, "Advice method must not be null");
157 		this.aspectJAdviceMethod = aspectJAdviceMethod;
158 		this.adviceInvocationArgumentCount = this.aspectJAdviceMethod.getParameterTypes().length;
159 		this.pointcut = pointcut;
160 		this.aspectInstanceFactory = aspectInstanceFactory;
161 	}
162 
163 
164 	/**
165 	 * Return the AspectJ-style advice method.
166 	 */
167 	public final Method getAspectJAdviceMethod() {
168 		return this.aspectJAdviceMethod;
169 	}
170 
171 	/**
172 	 * Return the AspectJ expression pointcut.
173 	 */
174 	public final AspectJExpressionPointcut getPointcut() {
175 		calculateArgumentBindings();
176 		return this.pointcut;
177 	}
178 
179 	/**
180 	 * Build a 'safe' pointcut that excludes the AspectJ advice method itself.
181 	 * @return a composable pointcut that builds on the original AspectJ expression pointcut
182 	 * @see #getPointcut()
183 	 */
184 	public final Pointcut buildSafePointcut() {
185 		Pointcut pc = getPointcut();
186 		MethodMatcher safeMethodMatcher = MethodMatchers.intersection(
187 				new AdviceExcludingMethodMatcher(this.aspectJAdviceMethod), pc.getMethodMatcher());
188 		return new ComposablePointcut(pc.getClassFilter(), safeMethodMatcher);
189 	}
190 
191 	/**
192 	 * Return the factory for aspect instances.
193 	 */
194 	public final AspectInstanceFactory getAspectInstanceFactory() {
195 		return this.aspectInstanceFactory;
196 	}
197 
198 	/**
199 	 * Return the ClassLoader for aspect instances.
200 	 */
201 	public final ClassLoader getAspectClassLoader() {
202 		return this.aspectInstanceFactory.getAspectClassLoader();
203 	}
204 
205 	@Override
206 	public int getOrder() {
207 		return this.aspectInstanceFactory.getOrder();
208 	}
209 
210 
211 	public void setAspectName(String name) {
212 		this.aspectName = name;
213 	}
214 
215 	@Override
216 	public String getAspectName() {
217 		return this.aspectName;
218 	}
219 
220 	/**
221 	 * Sets the <b>declaration order</b> of this advice within the aspect
222 	 */
223 	public void setDeclarationOrder(int order) {
224 		this.declarationOrder = order;
225 	}
226 
227 	@Override
228 	public int getDeclarationOrder() {
229 		return this.declarationOrder;
230 	}
231 
232 	/**
233 	 * Set by creator of this advice object if the argument names are known.
234 	 * <p>This could be for example because they have been explicitly specified in XML,
235 	 * or in an advice annotation.
236 	 * @param argNames comma delimited list of arg names
237 	 */
238 	public void setArgumentNames(String argNames) {
239 		String[] tokens = StringUtils.commaDelimitedListToStringArray(argNames);
240 		setArgumentNamesFromStringArray(tokens);
241 	}
242 
243 	public void setArgumentNamesFromStringArray(String... args) {
244 		this.argumentNames = new String[args.length];
245 		for (int i = 0; i < args.length; i++) {
246 			this.argumentNames[i] = StringUtils.trimWhitespace(args[i]);
247 			if (!isVariableName(this.argumentNames[i])) {
248 				throw new IllegalArgumentException(
249 						"'argumentNames' property of AbstractAspectJAdvice contains an argument name '" +
250 						this.argumentNames[i] + "' that is not a valid Java identifier");
251 			}
252 		}
253 		if (argumentNames != null) {
254 			if (aspectJAdviceMethod.getParameterTypes().length == argumentNames.length + 1) {
255 				// May need to add implicit join point arg name...
256 				Class<?> firstArgType = aspectJAdviceMethod.getParameterTypes()[0];
257 				if (firstArgType == JoinPoint.class ||
258 						firstArgType == ProceedingJoinPoint.class ||
259 						firstArgType == JoinPoint.StaticPart.class) {
260 					String[] oldNames = argumentNames;
261 					argumentNames = new String[oldNames.length + 1];
262 					argumentNames[0] = "THIS_JOIN_POINT";
263 					System.arraycopy(oldNames, 0, argumentNames, 1, oldNames.length);
264 				}
265 			}
266 		}
267 	}
268 
269 	public void setReturningName(String name) {
270 		throw new UnsupportedOperationException("Only afterReturning advice can be used to bind a return value");
271 	}
272 
273 	/**
274 	 * We need to hold the returning name at this level for argument binding calculations,
275 	 * this method allows the afterReturning advice subclass to set the name.
276 	 */
277 	protected void setReturningNameNoCheck(String name) {
278 		// name could be a variable or a type...
279 		if (isVariableName(name)) {
280 			this.returningName = name;
281 		}
282 		else {
283 			// assume a type
284 			try {
285 				this.discoveredReturningType = ClassUtils.forName(name, getAspectClassLoader());
286 			}
287 			catch (Throwable ex) {
288 				throw new IllegalArgumentException("Returning name '" + name  +
289 						"' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. " +
290 						"Root cause: " + ex);
291 			}
292 		}
293 	}
294 
295 	protected Class<?> getDiscoveredReturningType() {
296 		return this.discoveredReturningType;
297 	}
298 
299 	protected Type getDiscoveredReturningGenericType() {
300 		return this.discoveredReturningGenericType;
301 	}
302 
303 	public void setThrowingName(String name) {
304 		throw new UnsupportedOperationException("Only afterThrowing advice can be used to bind a thrown exception");
305 	}
306 
307 	/**
308 	 * We need to hold the throwing name at this level for argument binding calculations,
309 	 * this method allows the afterThrowing advice subclass to set the name.
310 	 */
311 	protected void setThrowingNameNoCheck(String name) {
312 		// name could be a variable or a type...
313 		if (isVariableName(name)) {
314 			this.throwingName = name;
315 		}
316 		else {
317 			// assume a type
318 			try {
319 				this.discoveredThrowingType = ClassUtils.forName(name, getAspectClassLoader());
320 			}
321 			catch (Throwable ex) {
322 				throw new IllegalArgumentException("Throwing name '" + name  +
323 						"' is neither a valid argument name nor the fully-qualified name of a Java type on the classpath. " +
324 						"Root cause: " + ex);
325 			}
326 		}
327 	}
328 
329 	protected Class<?> getDiscoveredThrowingType() {
330 		return this.discoveredThrowingType;
331 	}
332 
333 	private boolean isVariableName(String name) {
334 		char[] chars = name.toCharArray();
335 		if (!Character.isJavaIdentifierStart(chars[0])) {
336 			return false;
337 		}
338 		for (int i = 1; i < chars.length; i++) {
339 			if (!Character.isJavaIdentifierPart(chars[i])) {
340 				return false;
341 			}
342 		}
343 		return true;
344 	}
345 
346 
347 	/**
348 	 * Do as much work as we can as part of the set-up so that argument binding
349 	 * on subsequent advice invocations can be as fast as possible.
350 	 * <p>If the first argument is of type JoinPoint or ProceedingJoinPoint then we
351 	 * pass a JoinPoint in that position (ProceedingJoinPoint for around advice).
352 	 * <p>If the first argument is of type {@code JoinPoint.StaticPart}
353 	 * then we pass a {@code JoinPoint.StaticPart} in that position.
354 	 * <p>Remaining arguments have to be bound by pointcut evaluation at
355 	 * a given join point. We will get back a map from argument name to
356 	 * value. We need to calculate which advice parameter needs to be bound
357 	 * to which argument name. There are multiple strategies for determining
358 	 * this binding, which are arranged in a ChainOfResponsibility.
359 	 */
360 	public synchronized final void calculateArgumentBindings() {
361 		// The simple case... nothing to bind.
362 		if (this.argumentsIntrospected || this.adviceInvocationArgumentCount == 0) {
363 			return;
364 		}
365 
366 		int numUnboundArgs = this.adviceInvocationArgumentCount;
367 		Class<?>[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
368 		if (maybeBindJoinPoint(parameterTypes[0]) || maybeBindProceedingJoinPoint(parameterTypes[0])) {
369 			numUnboundArgs--;
370 		}
371 		else if (maybeBindJoinPointStaticPart(parameterTypes[0])) {
372 			numUnboundArgs--;
373 		}
374 
375 		if (numUnboundArgs > 0) {
376 			// need to bind arguments by name as returned from the pointcut match
377 			bindArgumentsByName(numUnboundArgs);
378 		}
379 
380 		this.argumentsIntrospected = true;
381 	}
382 
383 	private boolean maybeBindJoinPoint(Class<?> candidateParameterType) {
384 		if (candidateParameterType.equals(JoinPoint.class)) {
385 			this.joinPointArgumentIndex = 0;
386 			return true;
387 		}
388 		else {
389 			return false;
390 		}
391 	}
392 
393 	private boolean maybeBindProceedingJoinPoint(Class<?> candidateParameterType) {
394 		if (candidateParameterType.equals(ProceedingJoinPoint.class)) {
395 			if (!supportsProceedingJoinPoint()) {
396 				throw new IllegalArgumentException("ProceedingJoinPoint is only supported for around advice");
397 			}
398 			this.joinPointArgumentIndex = 0;
399 			return true;
400 		}
401 		else {
402 			return false;
403 		}
404 	}
405 
406 	protected boolean supportsProceedingJoinPoint() {
407 		return false;
408 	}
409 
410 	private boolean maybeBindJoinPointStaticPart(Class<?> candidateParameterType) {
411 		if (candidateParameterType.equals(JoinPoint.StaticPart.class)) {
412 			this.joinPointStaticPartArgumentIndex = 0;
413 			return true;
414 		}
415 		else {
416 			return false;
417 		}
418 	}
419 
420 	private void bindArgumentsByName(int numArgumentsExpectingToBind) {
421 		if (this.argumentNames == null) {
422 			this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod);
423 		}
424 		if (this.argumentNames != null) {
425 			// We have been able to determine the arg names.
426 			bindExplicitArguments(numArgumentsExpectingToBind);
427 		}
428 		else {
429 			throw new IllegalStateException("Advice method [" + this.aspectJAdviceMethod.getName() + "] " +
430 					"requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " +
431 					"the argument names were not specified and could not be discovered.");
432 		}
433 	}
434 
435 	/**
436 	 * Create a ParameterNameDiscoverer to be used for argument binding.
437 	 * <p>The default implementation creates a {@link DefaultParameterNameDiscoverer}
438 	 * and adds a specifically configured {@link AspectJAdviceParameterNameDiscoverer}.
439 	 */
440 	protected ParameterNameDiscoverer createParameterNameDiscoverer() {
441 		// We need to discover them, or if that fails, guess,
442 		// and if we can't guess with 100% accuracy, fail.
443 		DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
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 
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 	 */
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 	 */
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 	 */
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.
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 
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 	 */
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 	 */
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.
657 	protected JoinPointMatch getJoinPointMatch(ProxyMethodInvocation pmi) {
658 		return (JoinPointMatch) pmi.getUserAttribute(this.pointcut.getExpression());
659 	}
660 
661 
662 	@Override
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 
677 		public AdviceExcludingMethodMatcher(Method adviceMethod) {
678 			this.adviceMethod = adviceMethod;
679 		}
680 
681 		@Override
682 		public boolean matches(Method method, Class<?> targetClass) {
683 			return !this.adviceMethod.equals(method);
684 		}
685 
686 		@Override
687 		public boolean equals(Object other) {
688 			if (this == other) {
689 				return true;
690 			}
691 			if (!(other instanceof AdviceExcludingMethodMatcher)) {
692 				return false;
693 			}
694 			AdviceExcludingMethodMatcher otherMm = (AdviceExcludingMethodMatcher) other;
695 			return this.adviceMethod.equals(otherMm.adviceMethod);
696 		}
697 
698 		@Override
699 		public int hashCode() {
700 			return this.adviceMethod.hashCode();
701 		}
702 	}
703 
704 }