转-AOP动态代理解析3-增强方法的获取

https://www.cnblogs.com/wade-luffy/p/6076823.html

对于指定bean的增强方法的获取一定是包含两个步骤的:

  1. 获取所有的增强
  2. 寻找所有增强中使用于bean的增强并应用

那么findCandidateAdvisors与findAdvisorsThatCanApply便是做了这两件事情。当然,如果无法找到对便返回DO_NOT_PROXY,其中DO_NOT_PROXY=null.

protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource)  
{  
    List advisors = findEligibleAdvisors(beanClass, beanName);  
    if(advisors.isEmpty())  
        return DO_NOT_PROXY;  
    else  
        return advisors.toArray();  
}  
protected List findEligibleAdvisors(Class beanClass, String beanName)  
{  
  //获取所有的增强器
    List candidateAdvisors = findCandidateAdvisors();  
  //获取适合beanname的增强器
    List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);  
    extendAdvisors(eligibleAdvisors);  
    if(!eligibleAdvisors.isEmpty())  
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);  
    return eligibleAdvisors;  
}  

所有增强器的获取

由于分析的是使用注解进行的AOP,所以对于findCandidateAdvisors的实现其实是由AnnotationAwareAspectJAutoProxyCreator类完成的,我们继续跟踪AnnotationAwareAspectJAutoProxyCreator的findCandidateAdvisors方法。

protected List findCandidateAdvisors()  {  
    //当使用注解方式配置AOP的时候并不是丢弃了对XML配置的支持。  
    //在这里调用父类方法加载配置文件中的AOP声明  
    List advisors = super.findCandidateAdvisors();  
    //Build Advisors for all AspectJ aspects in the bean factory  
    advisors.addAll(aspectJAdvisorsBuilder.buildAspectJAdvisors());  
    return advisors;  
}

AnnotationAwareAspectJAutoProxyCreator间接继承了AbstractAdvisorAutoProxyCreator,在实现获取增强的方法中除了保留父类的获取配置文件中定义的增强外,同时添加了获取Bean的注解增强的功能,那么其实现正是由this.aspectJAdvisorsBuilder.buildAspectJAdvisors()来实现的。
(1)获取所有beanName,这一步骤中所有在beanFactory中注册的Bean都会被提取出来。
(2)遍历所有beanName,并找出声明AspectJ注解的类,进行进一步的处理。
(3)对标记为AspectJ注解的类进行增强器的提取。
(4)将提取结果加入缓存。

提取Advisor

现在来看看函数实现,对Spirng中所有的类进行分析,提取Advisor。

public List buildAspectJAdvisors()  
    {  
        List aspectNames = null;  
        synchronized(this){  
        aspectNames = aspectBeanNames;  
        if(aspectNames == null)  
        {  
            List advisors = new LinkedList();  
            aspectNames = new LinkedList();  
            //获取所有的beanName  
            String beanNames[] = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, java/lang/Object, true, false);  
            //循环所有的beanName找出对应的增强方法  
            for(String beanName : beanNames)  
            {  
                //不合法的bean则略过,由子类定义规则,默认返回true  
               if(!isEligibleBean(beanName))  
                    continue;  
                //获取对应的bean的类型  
                Class beanType = beanFactory.getType(beanName);  
                //如果存在Aspect注解  
               if(beanType == null || !advisorFactory.isAspect(beanType))  
                    continue;  
                aspectNames.add(beanName);  
                AspectMetadata amd = new AspectMetadata(beanType, beanName);  
                MetadataAwareAspectInstanceFactory factory;  
                if(amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON)  
                {  
                    factory = new BeanFactoryAspectInstanceFactory(beanFactory, beanName);  
                    //解析标记AspectJ注解中的增强方法  
                    List classAdvisors = advisorFactory.getAdvisors(factory);  
                    if(beanFactory.isSingleton(beanName))  
                        advisorsCache.put(beanName, classAdvisors);  
                    else  
                        aspectFactoryCache.put(beanName, factory);  
                    advisors.addAll(classAdvisors);  
                    continue;  
                }  
                if(beanFactory.isSingleton(beanName))  
                    throw new IllegalArgumentException((
                    new StringBuilder()).append("Bean with name '")
                                .append(beanName)
                                .append("' is a singleton, but aspect instantiation model is not singleton")
                                .toString());  
                factory = new PrototypeAspectInstanceFactory(beanFactory, beanName);  
                aspectFactoryCache.put(beanName, factory);  
                advisors.addAll(advisorFactory.getAdvisors(factory));  
            }  
  
            aspectBeanNames = aspectNames;  
            return advisors;  
         
    } 

至此,已经完成了Advisor的提取。

切点信息的获取

在上面的步骤中最为重要也最为繁杂的就是增强器的获取,而这一切功能委托给了getAdvisors方法去实现(this.advisorFactory.getAdvisors(factory))。

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
     //获取标记为AspectJ的类 
        final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();
     //获取标记为AspectJ的name
        final String aspectName = maaif.getAspectMetadata().getAspectName();
     //验证
        validate(aspectClass);
        final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
                new LazySingletonAspectInstanceFactoryDecorator(maaif);
        final List<Advisor> advisors = new LinkedList<Advisor>();
        for (Method method : getAdvisorMethods(aspectClass)) {
            Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }
     //如果寻找的增强器不为空而且又配置了增强延迟初始化那么需要在首位加入同步实例化增强器
        if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
            advisors.add(0, instantiationAdvisor);
        }
     //对DeclareParents注解的获取
        for (Field field : aspectClass.getDeclaredFields()) {
            Advisor advisor = getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }
        return advisors;
    }

函数中首先完成了增强器的获取,包括获取注解以及根据注解生成增强的步骤,然后考虑到在配置中可能会将增强配置成延迟初始化,那么需要在首位加入同步实例化增强器以保证增强使用之前的实例化,最后是对DeclareParents注解的获取。

普通增强器的获取逻辑通过getAdvisor方法实现,实现步骤包括对切点的注解的获取及根据注解信息生成增强。

(1)切点信息的获取。所谓获取切点信息就是指定注解的表达式信息的获取,如@Before("test()")

public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName)  
{  
    validate(aif.getAspectMetadata().getAspectClass());  
    //切点信息的获取  
    AspectJExpressionPointcut ajexp = getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());  
    if(ajexp == null)  
        return null;  
    else  
        //根据切点信息生成增强器  
        return new InstantiationModelAwarePointcutAdvisorImpl(this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);  
}  

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
    //获取方法上的注解  
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }
    //使用AspectJExpressionPointcut实例封装获取的信息
    AspectJExpressionPointcut ajexp =
            new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);
    //提取得到的注解中的表达式如:@Pointcut("execution(* *.*test*(..))")中的execution(* *.*test*(..))
    ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
    return ajexp;
}

protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method method) {
    //设置敏感的注解类
    Class<? extends Annotation>[] classesToLookFor = new Class[] {
            Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
    for (Class<? extends Annotation> c : classesToLookFor) {
        AspectJAnnotation foundAnnotation = findAnnotation(method, c);
        if (foundAnnotation != null) {
            return foundAnnotation;
        }
    }
    return null;
}
//获取指定方法上的注解并使用AspectJAnnotation封装
private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
    A result = AnnotationUtils.findAnnotation(method, toLookFor);
    if (result != null) {
        return new AspectJAnnotation<A>(result);
    }
    else {
        return null;
    }
}

(2)根据切点信息生成增强。所有的增强都有Advisor实现类InstantiationModelAwarePontcutAdvisorImpl统一封装。

public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp,
        MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName) {

    this.declaredPointcut = ajexp;
    this.method = method;
    this.atAspectJAdvisorFactory = af;
    this.aspectInstanceFactory = aif;
    this.declarationOrder = declarationOrderInAspect;
    this.aspectName = aspectName;

    if (aif.getAspectMetadata().isLazilyInstantiated()) {
        Pointcut preInstantiationPointcut =
                Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
        this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif);
        this.lazy = true;
    }
    else {
        this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
        this.pointcut = declaredPointcut;
        this.lazy = false;
    }
}

封装过程只是简单地将信息封装在类的实例中,所有的信息单纯地复制。在实例初始化的过程中还完成了对于增强器的初始化。因为不同的增强所体现的逻辑是不同的,比如@Before(“test()”)与After(“test()”)标签的不同就是增强器增强的位置不同,所以就需要不同的增强器来完成不同的逻辑,而根据注解中的信息初始化对应的增强器就是在instantiateAdvice函数中实现的。

private Advice instantiateAdvice(AspectJExpressionPointcut pcut)  
{  
    return atAspectJAdvisorFactory.getAdvice(method, pcut, aspectInstanceFactory, declarationOrder, aspectName);  
}  
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp, 
          MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName)  
    {  
        Class candidateAspectClass = aif.getAspectMetadata().getAspectClass();  
        validate(candidateAspectClass);  
        AbstractAspectJAdvisorFactory.AspectJAnnotation aspectJAnnotation = 
              AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);  
        if(aspectJAnnotation == null)  
            return null;  
       if(!isAspect(candidateAspectClass))  
            throw new AopConfigException(
            (new StringBuilder())
                  .append("Advice must be declared inside an aspect type: Offending method '")
                  .append(candidateAdviceMethod)
                  .append("' in class [")
                  .append(candidateAspectClass.getName())
                  .append("]").toString());  
        if(logger.isDebugEnabled())  
            logger.debug((new StringBuilder()).append("Found AspectJ method: ").append(candidateAdviceMethod).toString());  
        AbstractAspectJAdvice springAdvice;  
        switch(SwitchMap.org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory.AspectJAnnotationType
            [aspectJAnnotation.getAnnotationType().ordinal()])  
        {  
        case 1: // '\001'  
            springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);  
            break;  
  
        case 2: // '\002'  
            springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);  
            break;  
  
        case 3: // '\003'  
            springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);  
            AfterReturning afterReturningAnnotation = (AfterReturning)aspectJAnnotation.getAnnotation();  
            if(StringUtils.hasText(afterReturningAnnotation.returning()))  
                springAdvice.setReturningName(afterReturningAnnotation.returning());  
            break;  
  
        case 4: // '\004'  
            springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);  
            AfterThrowing afterThrowingAnnotation = (AfterThrowing)aspectJAnnotation.getAnnotation();  
            if(StringUtils.hasText(afterThrowingAnnotation.throwing()))  
                springAdvice.setThrowingName(afterThrowingAnnotation.throwing());  
            break;  
  
        case 5: // '\005'  
            springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);  
            break;  
  
        case 6: // '\006'  
            if(logger.isDebugEnabled())  
                logger.debug(
              (new StringBuilder()).append("Processing pointcut '")
                           .append(candidateAdviceMethod.getName())
                           .append("'").toString());  
            return null;  
  
        default:  
            throw new UnsupportedOperationException(
            (new StringBuilder()).append("Unsupported advice type on method ")
                         .append(candidateAdviceMethod).toString());  
        }  
        springAdvice.setAspectName(aspectName);  
        springAdvice.setDeclarationOrder(declarationOrderInAspect);  
        String argNames[] = parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);  
        if(argNames != null)  
            springAdvice.setArgumentNamesFromStringArray(argNames);  
        springAdvice.calculateArgumentBindings();  
        return springAdvice;  
    }  
}

从函数中可以看到,Spring会根据不同的注解生成不同的增强器,例如AtBefore会对应AspectJMethodBeforeAdvice。

增加同步实例化增强器

如果寻找的增强器不为空而且又配置了增强延迟初始化,那么就需要在首位加入同步实例化增强器。

 protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor {
        public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif) {
            super(aif.getAspectMetadata().getPerClausePointcut(), new MethodBeforeAdvice() {
                @Override
                public void before(Method method, Object[] args, Object target) {
                    // Simply instantiate the aspect
                    aif.getAspectInstance();
                }
            });
        }
    }

获取DeclareParents注解

DeclareParents主要用于引介增强的注解形式的实现,而其实现方式驭普通增强很类似,只不过使用DeclareParentsAdvisor对功能进行封装。

引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过引介功能,可以动态的未该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。

private Advisor getDeclareParentsAdvisor(Field introductionField)  
{  
    DeclareParents declareParents = (DeclareParents)introductionField.getAnnotation(org/aspectj/lang/annotation/DeclareParents);  
    if(declareParents == null)  
        return null;  
    if("org/aspectj/lang/annotation/DeclareParents".equals(declareParents.defaultImpl()))  
        throw new IllegalStateException("defaultImpl must be set on DeclareParents");  
    else  
        return new DeclareParentsAdvisor(introductionField.getType(), declareParents.value(), declareParents.defaultImpl());  
}  

至此已经完成了所有增强器的解析,但是对于所有增强器来讲,并不一定都适用于当前的Bean,还要挑取除适合的增强器,也就是满足我们配置的通配符的增强器。具体的实现在findAdvisorsThatCanApply中。

获取合适的增强器

findAdvisorsThatCanApply函数的主要功能是寻找所有增强器中适用于当前class的增强器。对于真是的匹配在canApply中实现。

protected List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class beanClass, String beanName) {
    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null);
    }
}
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) {
        if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
            eligibleAdvisors.add(candidate);
        }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            continue;
        }
        //对于普通bean的处理  
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    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 = clazz.getMethods();
        for (Method method : methods) {
            if ((introductionAwareMethodMatcher != null &&
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }
    return false;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354