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

简介: 对于指定bean的增强方法的获取一定是包含两个步骤的: 获取所有的增强 寻找所有增强中使用于bean的增强并应用 那么findCandidateAdvisors与findAdvisorsThatCanApply便是做了这两件事情。

对于指定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;
}

 

 

 

 

 

 

 

 

 

目录
相关文章
|
1月前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
3月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
10月前
|
监控 安全 网络安全
深入解析PDCERF:网络安全应急响应的六阶段方法
PDCERF是网络安全应急响应的六阶段方法,涵盖准备、检测、抑制、根除、恢复和跟进。本文详细解析各阶段目标与操作步骤,并附图例,助读者理解与应用,提升组织应对安全事件的能力。
1507 89
|
11月前
|
存储 Java 开发者
浅析JVM方法解析、创建和链接
上一篇文章《你知道Java类是如何被加载的吗?》分析了HotSpot是如何加载Java类的,本文再来分析下Hotspot又是如何解析、创建和链接类方法的。
523 132
|
11月前
|
安全 Ubuntu Shell
深入解析 vsftpd 2.3.4 的笑脸漏洞及其检测方法
本文详细解析了 vsftpd 2.3.4 版本中的“笑脸漏洞”,该漏洞允许攻击者通过特定用户名和密码触发后门,获取远程代码执行权限。文章提供了漏洞概述、影响范围及一个 Python 脚本,用于检测目标服务器是否受此漏洞影响。通过连接至目标服务器并尝试登录特定用户名,脚本能够判断服务器是否存在该漏洞,并给出相应的警告信息。
700 84
|
9月前
|
编解码 缓存 Prometheus
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
本期内容为「ximagine」频道《显示器测试流程》的规范及标准,我们主要使用Calman、DisplayCAL、i1Profiler等软件及CA410、Spyder X、i1Pro 2等设备,是我们目前制作内容数据的重要来源,我们深知所做的仍是比较表面的活儿,和工程师、科研人员相比有着不小的差距,测试并不复杂,但是相当繁琐,收集整理测试无不花费大量时间精力,内容不完善或者有错误的地方,希望大佬指出我们好改进!
640 16
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
|
8月前
|
JSON 监控 网络协议
Bilibili直播信息流:连接方法与数据解析
本文详细介绍了自行实现B站直播WebSocket连接的完整流程。解析了基于WebSocket的应用层协议结构,涵盖认证包构建、心跳机制维护及数据包解析步骤,为开发者定制直播数据监控提供了完整技术方案。
|
8月前
|
安全 IDE Java
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
259 1
|
8月前
|
传感器 监控 Java
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
348 5
|
9月前
|
XML 安全 Java
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
438 24

推荐镜像

更多
  • DNS