@Override
  public boolean matches(Method method, Class<?> targetClass, boolean beanHasIntroductions) {
    checkReadyToMatch();
    Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
    ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);

    // Special handling for this, target, @this, @target, @annotation
    // in Spring - we can optimize since we know we have exactly this class,
    // and there will never be matching subclass at runtime.
    if (shadowMatch.alwaysMatches()) {
      return true;
    } else if (shadowMatch.neverMatches()) {
      return false;
    } else {
      // the maybe case
      if (beanHasIntroductions) {
        return true;
      }
      // A match test returned maybe - if there are any subtype sensitive variables
      // involved in the test (this, target, at_this, at_target, at_annotation) then
      // we say this is not a match as in Spring there will never be a different
      // runtime subtype.
      RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
      return (!walker.testsSubtypeSensitiveVars()
          || walker.testTargetInstanceOfResidue(targetClass));
    }
  }
 private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
   // Avoid lock contention for known Methods through concurrent access...
   ShadowMatch shadowMatch = this.shadowMatchCache.get(targetMethod);
   if (shadowMatch == null) {
     synchronized (this.shadowMatchCache) {
       // Not found - now check again with full lock...
       PointcutExpression fallbackExpression = null;
       Method methodToMatch = targetMethod;
       shadowMatch = this.shadowMatchCache.get(targetMethod);
       if (shadowMatch == null) {
         try {
           shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
         } catch (ReflectionWorldException ex) {
           // Failed to introspect target method, probably because it has been loaded
           // in a special ClassLoader. Let's try the declaring ClassLoader instead...
           try {
             fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
             if (fallbackExpression != null) {
               shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
             }
           } catch (ReflectionWorldException ex2) {
             fallbackExpression = null;
           }
         }
         if (shadowMatch == null && targetMethod != originalMethod) {
           methodToMatch = originalMethod;
           try {
             shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
           } catch (ReflectionWorldException ex3) {
             // Could neither introspect the target class nor the proxy class ->
             // let's try the original method's declaring class before we give up...
             try {
               fallbackExpression =
                   getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
               if (fallbackExpression != null) {
                 shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
               }
             } catch (ReflectionWorldException ex4) {
               fallbackExpression = null;
             }
           }
         }
         if (shadowMatch == null) {
           shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
         } else if (shadowMatch.maybeMatches() && fallbackExpression != null) {
           shadowMatch =
               new DefensiveShadowMatch(
                   shadowMatch, fallbackExpression.matchesMethodExecution(methodToMatch));
         }
         this.shadowMatchCache.put(targetMethod, shadowMatch);
       }
     }
   }
   return shadowMatch;
 }
  @Override
  public boolean matches(Method method, Class<?> targetClass, Object[] args) {
    checkReadyToMatch();
    ShadowMatch shadowMatch =
        getShadowMatch(AopUtils.getMostSpecificMethod(method, targetClass), method);
    ShadowMatch originalShadowMatch = getShadowMatch(method, method);

    // Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
    // consistent with return of MethodInvocationProceedingJoinPoint
    ProxyMethodInvocation pmi = null;
    Object targetObject = null;
    Object thisObject = null;
    try {
      MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
      targetObject = mi.getThis();
      if (!(mi instanceof ProxyMethodInvocation)) {
        throw new IllegalStateException(
            "MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
      }
      pmi = (ProxyMethodInvocation) mi;
      thisObject = pmi.getProxy();
    } catch (IllegalStateException ex) {
      // No current invocation...
      // TODO: Should we really proceed here?
      if (logger.isDebugEnabled()) {
        logger.debug("Could not access current invocation - matching with limited context: " + ex);
      }
    }

    try {
      JoinPointMatch joinPointMatch = shadowMatch.matchesJoinPoint(thisObject, targetObject, args);

      /*
       * Do a final check to see if any this(TYPE) kind of residue match. For
       * this purpose, we use the original method's (proxy method's) shadow to
       * ensure that 'this' is correctly checked against. Without this check,
       * we get incorrect match on this(TYPE) where TYPE matches the target
       * type but not 'this' (as would be the case of JDK dynamic proxies).
       * <p>See SPR-2979 for the original bug.
       */
      if (pmi != null) { // there is a current invocation
        RuntimeTestWalker originalMethodResidueTest = getRuntimeTestWalker(originalShadowMatch);
        if (!originalMethodResidueTest.testThisInstanceOfResidue(thisObject.getClass())) {
          return false;
        }
        if (joinPointMatch.matches()) {
          bindParameters(pmi, joinPointMatch);
        }
      }

      return joinPointMatch.matches();
    } catch (Throwable ex) {
      if (logger.isDebugEnabled()) {
        logger.debug(
            "Failed to evaluate join point for arguments "
                + Arrays.asList(args)
                + " - falling back to non-match",
            ex);
      }
      return false;
    }
  }