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