1. 程式人生 > >Spring基於註解形式的 AOP的原理流程及原始碼解析(三)

Spring基於註解形式的 AOP的原理流程及原始碼解析(三)

此篇部落格主要講解Spring如何驗證將要例項化的Bean是否應該被代理,生成代理物件的時機問題。

在第二篇部落格中,Spring對容器內所有的標識了@Aspect註解的的類的切面方法(標識了@Around, @Before, @After, @AfterReturning, @AfterThrowing的方法)生成切面Advisor,切面由切入點Pointcut和通知組成,切入點一般是ExpressionPointcut,及帶表示式的切入點,由註解上的value或pointcut屬性來指定;通知一般就是被織入的方法,就是註解標識的方法。

Spring代理Bean預設是singleton形式,即多個織入方法織入同一個代理物件,而不是每個方法生成一個物件。傳統的AOP是每個方法生成一個代理物件。詳情可檢視@Aspect的value屬性。如果被代理物件的scope不是singleton,則需要在value屬性上指定代理的型別,比如是perTarget等。

基於註解形式的AOP的代理物件生成的時機一般是在每個Bean初始化之後,及執行BeanPostProcessor的postProcessAfterInitialization方法,可看AbstractAutoProxyCreator的方法結構。如果物件的scope非singleton,那麼生成代理物件的時機肯定在postProcessAfterInitialization;如果物件的scope是singleton,那麼可能在postProcessAfterInitialization,也可能在getEarlyBeanReference方法。如果被代理的Bean觸發了迴圈引用circular references,即Bean A注入了Bean B,而B內也注入了A,那麼在解析B的時候會觸發A的getEarlyBeanReference方法,如果A符合被代理的條件,則會直接生產代理物件,將代理物件注入B的屬性中。在這種情況下有危險,B本來想注入A,但是結果注入的是A的代理物件,此時A還是一個原始的物件,還未注入屬性及執行初始化方法,所以有很大程度上會報異常。結論 : 動態代理scope = singleton時請注意避免迴圈引用。

接下來將生成代理物件的程式碼:
程式碼塊1

public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
        implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    //如果scope = singleton的Bean觸發circular references,會在此步生成代理例項,生成時機比正常的早得多
    public Object getEarlyBeanReference(Object bean, String beanName) throws
BeansException { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { this.earlyProxyReferences.add(cacheKey); } return wrapIfNecessary(bean, beanName, cacheKey); } public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); //大部分的動態代理物件均在此步驟生成,此步驟是Bean例項化的最後一步 if (!this.earlyProxyReferences.contains(cacheKey)) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; } protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (beanName != null && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // Create proxy if we have advice. //校驗此類是否應該被代理,也就是將此類和所有的ExpressionPointcut匹配,匹配程式碼解析看下一塊(程式碼塊2) Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { this.advisedBeans.put(cacheKey, Boolean.TRUE); Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } }

驗證在例項化的Bean是否應該被代理,如果應該則發揮那些符合條件的待織入切面Advisor:
程式碼塊2

public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {

    protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }

    protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        //獲取容器中的所有切面
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        //驗證beanClass是否該被代理,如果應該,則返回所有符合條件的待織入切面
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

    protected List<Advisor> findAdvisorsThatCanApply(
            List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {

        ProxyCreationContext.setCurrentProxiedBeanName(beanName);
        try { //匹配過程,看下一塊程式碼(程式碼塊3)
            return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
        }
        finally {
            ProxyCreationContext.setCurrentProxiedBeanName(null);
        }
    }

}

AopUtils工具類迴圈的將每個Advisor與BeanClass匹配,如果匹配上就方法返回的集合中,
驗證步驟:

  1. 在類上做精確匹配,如果匹配規則定義在方法上,則返回maybetrue,如果規則定義在類上,不匹配則直接返回false
  2. 當類上返回的結果為true或者maybetrue,再進行方法的匹配。方法的匹配也是先做模糊匹配,如果返回的為nevermatch或者alwaysmatche,則得到了結果。
  3. 如果返回的結果不能確定為true或false,則需要根據實際的去驗證,比如args(…)則驗證方法的引數是否有指定的型別,@args則驗證方法的引數是否標識了指定的註解
  4. 在方法上做模糊匹配的原因是一個類從父類繼承,實現介面及自身的方法很多,如果能提前做一個大致的預估,如果預估的結果是肯定不匹配或不匹配,則可以省去對其所有方法的匹配解析。

程式碼塊3

public abstract class AopUtils {

    public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        if (candidateAdvisors.isEmpty()) {
            return candidateAdvisors;
        }
        List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
        for (Advisor candidate : candidateAdvisors) {
            //基於註解形式生成的Advisor均是PointcutAdvisor,所以條件均不符合
            if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
                eligibleAdvisors.add(candidate);
            }
        }
        boolean hasIntroductions = !eligibleAdvisors.isEmpty();
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor) {
                // already processed
                continue;
            }
            if (canApply(candidate, clazz, hasIntroductions)) {
                eligibleAdvisors.add(candidate);
            }
        }
        return eligibleAdvisors;
    }

    public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
        if (advisor instanceof IntroductionAdvisor) {
            return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
        }
        else if (advisor instanceof PointcutAdvisor) {
            PointcutAdvisor pca = (PointcutAdvisor) advisor;
            //pca.getPointcut()返回的是一個AspectJExpressionPointcut型別物件
            return canApply(pca.getPointcut(), targetClass, hasIntroductions);
        }
        else {
            // It doesn't have a pointcut so we assume it applies.
            return true;
        }
    }

    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
        Assert.notNull(pc, "Pointcut must not be null");
        //先對類做匹配,如果結果為false則說明不匹配,程式碼詳解看程式碼塊4
        if (!pc.getClassFilter().matches(targetClass)) {
            return false;
        }
        //再對方法做匹配,有些匹配規則只作用在類上,那麼方法的匹配確定返回true,
        //很多規則是針對方法的代理,所以需要繼續對合適的類裡的所有方法做驗證
        MethodMatcher methodMatcher = pc.getMethodMatcher();
        //methodMatcher 一般是AspectJExpressionPointcut物件自身,所有此步不符合
        if (methodMatcher == MethodMatcher.TRUE) {  
            // No need to iterate the methods if we're matching any method anyway...
            return true;
        }

        IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
        if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
            introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
        }

        Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
        classes.add(targetClass);
        for (Class<?> clazz : classes) {
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
            for (Method method : methods) {
                //獲取類所實現的所有介面和所有類層級的方法,迴圈驗證
                if ((introductionAwareMethodMatcher != null &&
                        introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
                        methodMatcher.matches(method, targetClass)) {
                    return true;
                }
            }
        }

        return false;
    }

    public boolean matches(Method method, Class<?> targetClass) {
        return matches(method, targetClass, false);
    }

    public boolean matches(Method method, Class<?> targetClass, boolean beanHasIntroductions) {
        checkReadyToMatch();
        Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
        //getShadowMatch方法的呼叫繁雜,有點類似Class的匹配,最終也會呼叫到Pointcut的match(Shadow shadow)方法
        //Pointcut對方法的匹配看下一個方法
        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.
        //this, target, @this, @target, @annotation這些匹配規則都是匹配類的,當類的匹配通過時,方法均通過
        if (shadowMatch.alwaysMatches()) {
            return true;
        }
        else if (shadowMatch.neverMatches()) {
            return false;
        }
        else {
            // the maybe case
            if (beanHasIntroductions) {
                return true;
            }
            //如果FuzzyBoolean 即不確定錯誤,也不確定是正確的,那麼就需要實際的驗證,
            //比如args(...)則驗證方法的引數是否有指定的型別,@args則驗證方法的引數是否標識了指定的註解
            // 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));
        }
    }

public abstract class Pointcut extends PatternNode {

    public final FuzzyBoolean match(Shadow shadow) {
        if (shadow.shadowId == lastMatchedShadowId) {
            return lastMatchedShadowResult;
        }
        FuzzyBoolean ret;
        // this next test will prevent a lot of un-needed matching going on....
        if (shadow.getKind().isSet(couldMatchKinds())) {
            //不同的Pointcut例項對此方法做了不同的實現,比如args,this,target等等,
            //不是實實在在的根據表示式去驗證方法,而僅僅只是粗略的返回一個模糊的結果,比如可能為真,也可能為假
            ret = matchInternal(shadow);
        } else {
            ret = FuzzyBoolean.NO;
        }
        lastMatchedShadowId = shadow.shadowId;
        lastMatchedShadowResult = ret;
        return ret;
    }

}

}

表示式形式的切入點對類的匹配驗證:
程式碼塊4

public class AspectJExpressionPointcut extends AbstractExpressionPointcut
        implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {

    public boolean matches(Class<?> targetClass) {
        checkReadyToMatch(); //確保生成表示式解析的Context環境
        try {
            try {  //根據表示式的解析例項,驗證此類是否匹配,程式碼解析看下一塊程式碼(程式碼塊6)
                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 (Throwable ex) {
            logger.debug("PointcutExpression matching rejected target class", ex);
        }
        return false;
    }

    private void checkReadyToMatch() {
        if (getExpression() == null) {
            throw new IllegalStateException("Must set property 'expression' before attempting to match");
        }
        if (this.pointcutExpression == null) {
            this.pointcutClassLoader = determinePointcutClassLoader();
            //這一步很重要,根據切面註解的value屬性,生成切入點表示式例項,程式碼解析看下一塊程式碼(程式碼塊5)
            this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
        }
    }

}

根據切面註解的value值,比如@After(“@args(….)”),生成不同例項的切入點表示式例項,即Pointcut實現類,切入點的實現類有很多,但Spring並不是每種都支援,支援的型別可以在AspectJExpressionPointcut的SUPPORTED_PRIMITIVES屬性中看到。
程式碼塊5

public class AspectJExpressionPointcut extends AbstractExpressionPointcut
        implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {

    private void checkReadyToMatch() {
        if (getExpression() == null) {
            throw new IllegalStateException("Must set property 'expression' before attempting to match");
        }
        if (this.pointcutExpression == null) {
            this.pointcutClassLoader = determinePointcutClassLoader();
            //第一步
            this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
        }
    }

    private PointcutExpression buildPointcutExpression(ClassLoader classLoader) {
        PointcutParser parser = initializePointcutParser(classLoader);
        PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
        for (int i = 0; i < pointcutParameters.length; i++) {
            pointcutParameters[i] = parser.createPointcutParameter(
                    this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
        } 
        //第二步 
        return parser.parsePointcutExpression(replaceBooleanOperators(getExpression()),
                this.pointcutDeclarationScope, pointcutParameters);
    }

}

public class PointcutParser {

    public PointcutExpression parsePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters)
            throws UnsupportedPointcutPrimitiveException, IllegalArgumentException {
        PointcutExpressionImpl pcExpr = null;
        try {
            //第三步
            Pointcut pc = resolvePointcutExpression(expression, inScope, formalParameters);
            pc = concretizePointcutExpression(pc, inScope, formalParameters);
            validateAgainstSupportedPrimitives(pc, expression); // again, because we have now followed any ref'd pcuts
            pcExpr = new PointcutExpressionImpl(pc, expression, formalParameters, getWorld());
        } catch (ParserException pEx) {
            throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
        } catch (ReflectionWorld.ReflectionWorldException rwEx) {
            throw new IllegalArgumentException(rwEx.getMessage());
        }
        return pcExpr;
    }

    protected Pointcut resolvePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters) {
        try {
            PatternParser parser = new PatternParser(expression);
            parser.setPointcutDesignatorHandlers(pointcutDesignators, world);
            Pointcut pc = parser.parsePointcut();  //第四步
            validateAgainstSupportedPrimitives(pc, expression);
            IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope), formalParameters);
            pc = pc.resolve(resolutionScope);
            return pc;
        } catch (ParserException pEx) {
            throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
        }
    }

    public Pointcut parsePointcut() {
        //第五步,最關鍵的一步
        Pointcut p = parseAtomicPointcut();
        if (maybeEat("&&")) {
            p = new AndPointcut(p, parseNotOrPointcut());
        }

        if (maybeEat("||")) {
            p = new OrPointcut(p, parsePointcut());
        }

        return p;
    }
    //根據切面註解的value屬性的值上的特殊字串,解析成不同的Pointcut的例項
    private Pointcut parseAtomicPointcut() {
        if (maybeEat("!")) {  //解析字串以"!"開頭
            int startPos = tokenSource.peek(-1).getStart();
            Pointcut p = new NotPointcut(parseAtomicPointcut(), startPos);
            return p;
        }
        if (maybeEat("(")) {  //解析value值以"("開頭
            Pointcut p = parsePointcut();
            eat(")");
            return p;
        }
        if (maybeEat("@")) { //解析value值以"@"開頭
            int startPos = tokenSource.peek().getStart();
            Pointcut p = parseAnnotationPointcut();
            int endPos = tokenSource.peek(-1).getEnd();
            p.setLocation(sourceContext, startPos, endPos);
            return p;
        } 
        int startPos = tokenSource.peek().getStart();
        Pointcut p = parseSinglePointcut();  //如果均不是
        int endPos = tokenSource.peek(-1).getEnd();
        p.setLocation(sourceContext, startPos, endPos);
        return p;
    }
    //解析value值以"@"開頭
    public Pointcut parseAnnotationPointcut() {
        int start = tokenSource.getIndex();
        IToken t = tokenSource.peek();
        String kind = parseIdentifier();
        IToken possibleTypeVariableToken = tokenSource.peek();
        String[] typeVariables = maybeParseSimpleTypeVariableList();
        if (typeVariables != null) {
            String message = "(";
            assertNoTypeVariables(typeVariables, message, possibleTypeVariableToken);
        }
        tokenSource.setIndex(start);
        if (kind.equals("annotation")) {
            return parseAtAnnotationPointcut();
        } else if (kind.equals("args")) {
            return parseArgsAnnotationPointcut();
        } else if (kind.equals("this") || kind.equals("target")) {
            return parseThisOrTargetAnnotationPointcut();
        } else if (kind.equals("within")) {
            return parseWithinAnnotationPointcut();
        } else if (kind.equals("withincode")) {
            return parseWithinCodeAnnotationPointcut();
        }
        throw new ParserException("pointcut name", t);
    }
    //解析普通的單例的切入點例項
    public Pointcut parseSinglePointcut() {
        int start = tokenSource.getIndex();
        IToken t = tokenSource.peek();
        Pointcut p = t.maybeGetParsedPointcut();
        if (p != null) {
            tokenSource.next();
            return p;
        }

        String kind = parseIdentifier();
        // IToken possibleTypeVariableToken = tokenSource.peek();
        // String[] typeVariables = maybeParseSimpleTypeVariableList();
        if (kind.equals("execution") || kind.equals("call") || kind.equals("get") || kind.equals("set")) {
            p = parseKindedPointcut(kind);
        } else if (kind.equals("args")) {
            p = parseArgsPointcut();
        } else if (kind.equals("this")) {
            p = parseThisOrTargetPointcut(kind);
        } else if (kind.equals("target")) {
            p = parseThisOrTargetPointcut(kind);
        } else if (kind.equals("within")) {
            p = parseWithinPointcut();
        } else if (kind.equals("withincode")) {
            p = parseWithinCodePointcut();
        } else if (kind.equals("cflow")) {
            p = parseCflowPointcut(false);
        } else if (kind.equals("cflowbelow")) {
            p = parseCflowPointcut(true);
        } else if (kind.equals("adviceexecution")) {
            eat("(");
            eat(")");
            p = new KindedPointcut(Shadow.AdviceExecution, new SignaturePattern(Member.ADVICE, ModifiersPattern.ANY,
                    TypePattern.ANY, TypePattern.ANY, NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY,
                    AnnotationTypePattern.ANY));
        } else if (kind.equals("handler")) {
            eat("(");
            TypePattern typePat = parseTypePattern(false, false);
            eat(")");
            p = new HandlerPointcut(typePat);
        } else if (kind.equals("lock") || kind.equals("unlock")) {
            p = parseMonitorPointcut(kind);
        } else if (kind.equals("initialization")) {
            eat("(");
            SignaturePattern sig = parseConstructorSignaturePattern();
            eat(")");
            p = new KindedPointcut(Shadow.Initialization, sig);
        } else if (kind.equals("staticinitialization")) {
            eat("(");
            TypePattern typePat = parseTypePattern(false, false);
            eat(")");
            p = new KindedPointcut(Shadow.StaticInitialization, new SignaturePattern(Member.STATIC_INITIALIZATION,
                    ModifiersPattern.ANY, TypePattern.ANY, typePat, NamePattern.ANY, TypePatternList.EMPTY, ThrowsPattern.ANY,
                    AnnotationTypePattern.ANY));
        } else if (kind.equals("preinitialization")) {
            eat("(");
            SignaturePattern sig = parseConstructorSignaturePattern();
            eat(")");
            p = new KindedPointcut(Shadow.PreInitialization, sig);
        } else if (kind.equals("if")) {
            // - annotation style only allows if(), if(true) or if(false)
            // - if() means the body of the annotated method represents the if expression
            // - anything else is an error because code cannot be put into the if()
            // - code style will already have been processed and the call to maybeGetParsedPointcut()
            // at the top of this method will have succeeded.
            eat("(");
            if (maybeEatIdentifier("true")) {
                eat(")");
                p = new IfPointcut.IfTruePointcut();
            } else if (maybeEatIdentifier("false")) {
                eat(")");
                p = new IfPointcut.IfFalsePointcut();
            } else {
                if (!maybeEat(")")) {
                    throw new ParserException(
                            "in annotation style, if(...) pointcuts cannot contain code. Use if() and put the code in the annotated method",
                            t);
                }
                // TODO - Alex has some token stuff going on here to get a readable name in place of ""...
                p = new IfPointcut("");
            }
        } else {
            boolean matchedByExtensionDesignator = false;
            // see if a registered handler wants to parse it, otherwise
            // treat as a reference pointcut
            for (PointcutDesignatorHandler pcd : pointcutDesignatorHandlers) {
                if (pcd.getDesignatorName().equals(kind)) {
                    p = parseDesignatorPointcut(pcd);
                    matchedByExtensionDesignator = true;
                }

            }
            if (!matchedByExtensionDesignator) {
                tokenSource.setIndex(start);
                p = parseReferencePointcut();
            }
        }
        return p;
    }
}

切入點表示式例項即PointcutExpression驗證一個類是否匹配表示式,主要是根據其內部持有的Pointcut例項來匹配驗證,而Pointcut例項則根據切面註解的value值如@After(“@args(…..)”)來生成相應的Pointcut實現類,以這個實現類來驗證:
程式碼塊6

public class PointcutExpressionImpl implements PointcutExpression {

    private Pointcut pointcut;
    private String expression;

    public boolean couldMatchJoinPointsInType(Class aClass) {
        ResolvedType matchType = world.resolve(aClass.getName());
        if (matchType.isMissing() && (world instanceof ReflectionWorld)) {
            // Class is a generated class that cannot be 'looked up' via getResource.
            // For example a proxy or lambda.
            // Use the class itself in this case
            matchType = ((ReflectionWorld)world).resolveUsingClass(aClass);
        }
        ReflectionFastMatchInfo info = new ReflectionFastMatchInfo(matchType, null, this.matchContext, world);
        //關鍵看這步,不同的實現類返回不同的值,maybeTrue代表是可能為真,比如很多適配的原則是在方法上的,那麼這步就會返回true
        //有些規則是要在類上做匹配的,則會在此步驟做驗證,通過後在方法上的驗證直接返回true
        boolean couldMatch = pointcut.fastMatch(info).maybeTrue();
        if (MATCH_INFO) {
            System.out.println("MATCHINFO: fast match for '" + this.expression + "' against '" + aClass.getName() + "': "
                    + couldMatch);
        }
        return couldMatch;
    }

}