@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));
    }
  }
 @Override
 public boolean matches(Class<?> targetClass) {
   checkReadyToMatch();
   try {
     try {
       return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
     } catch (ReflectionWorldException ex) {
       logger.debug(
           "PointcutExpression matching rejected target class - trying fallback expression", ex);
       // Actually this is still a "maybe" - treat the pointcut as dynamic if we don't know enough
       // yet
       PointcutExpression fallbackExpression = getFallbackPointcutExpression(targetClass);
       if (fallbackExpression != null) {
         return fallbackExpression.couldMatchJoinPointsInType(targetClass);
       }
     }
   } catch (BCException ex) {
     logger.debug("PointcutExpression matching rejected target class", ex);
   }
   return false;
 }
  @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;
    }
  }
 @Override
 public boolean isRuntime() {
   checkReadyToMatch();
   return this.pointcutExpression.mayNeedDynamicTest();
 }
 /** Return the underlying AspectJ pointcut expression. */
 public PointcutExpression getPointcutExpression() {
   checkReadyToMatch();
   return this.pointcutExpression;
 }
 @Override
 public MethodMatcher getMethodMatcher() {
   checkReadyToMatch();
   return this;
 }
 @Override
 public ClassFilter getClassFilter() {
   checkReadyToMatch();
   return this;
 }