@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;
    }
  }