资讯专栏INFORMATION COLUMN

Spring AOP 源码初窥(三)扫描Advice与Bean匹配

oysun / 1023人阅读

摘要:版本如何扫描接上一回,讲到了方法,该方法的目的是获取并生成。其中英文为源码注释。那么,以上便是通过扫描配置并生成的过程了。一些总结读到这儿,如何扫描配置,生成类,并匹配对应的整个流程已经很清楚了。

版本

spring 5.0.8.BUILD-SNAPSHOT

aspectjweaver 1.8.13

如何扫描Advice

接上一回,讲到了getAdvicesAndAdvisorsForBean方法,该方法的目的是获取并生成Advisor Bean。其中包含了扫描通过@Aspect注解配置且与Bean方法的匹配的Advice,也是本章主要讲的内容

getAdvicesAndAdvisorsForBean
/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java

@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
        Class beanClass, String beanName, @Nullable TargetSource targetSource) {

    List advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}

/**
 * Find all eligible Advisors for auto-proxying this class.
 * @param beanClass the clazz to find advisors for
 * @param beanName the name of the currently proxied bean
 * @return the empty List, not {@code null},
 * if there are no pointcuts or interceptors
 * @see #findCandidateAdvisors
 * @see #sortAdvisors
 * @see #extendAdvisors
 */
protected List findEligibleAdvisors(Class beanClass, String beanName) {
    List candidateAdvisors = findCandidateAdvisors();
    List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

....

关注findEligibleAdvisors方法可以清楚地知道这里主要分四步:

findCandidateAdvisors (获取候选Advisor)

findAdvisorsThatCanApply (获取适用于bean的Advisor: 例如Pointcut匹配)

extendAdvisors (特殊处理,这里不赘述)

sortAdvisors (排序,不赘述)

什么是Advisor? 首先,Advice是增强方法,即@Around, @Before等注解修饰的方法。而Advisor则是在Advice之上再包了一层。例如PointcutAdvisor则包有Advice和Pointcut

下面接着看findCandidateAdvisors和findAdvisorsThatCanApply

findCandidateAdvisors
/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java
....
/**
 * Find all candidate Advisors to use in auto-proxying.
 * @return the List of candidate Advisors
 */
protected List findCandidateAdvisors() {
    Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
    return this.advisorRetrievalHelper.findAdvisorBeans();
}
....
/org/springframework/aop/aspectj/annotation/AnnotationAwareAspectJAutoProxyCreator.java
....
@Override
protected List findCandidateAdvisors() {
    // Add all the Spring advisors found according to superclass rules.
    List advisors = super.findCandidateAdvisors();
    // Build Advisors for all AspectJ aspects in the bean factory.
    if (this.aspectJAdvisorsBuilder != null) {
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
}
....

这里findCandidateAdvisors在AbstractAdvisorAutoProxyCreator中有实现,同时被AnnotationAwareAspectJAutoProxyCreator重写了。不过可以看到重写的方法中先调用了super.findCandidateAdvisor,因此两个方法的代码都被执行了。

this.advisorRetrievalHelper.findAdvisorBeans (该方法主要从BeanFactory中获取Advisor Bean)

this.aspectJAdvisorsBuilder.buildAspectJAdvisors (从所有Bean中获取@Aspect配置的Bean并创建Advisor,也是我们关注的内容,下面细讲)

buildAspectJAdvisors
/org/springframework/aop/aspectj/annotation/BeanFactoryAspectJAdvisorsBuilder.java
....
/**
 * Look for AspectJ-annotated aspect beans in the current bean factory,
 * and return to a list of Spring AOP Advisors representing them.
 * 

Creates a Spring Advisor for each AspectJ advice method. * @return the list of {@link org.springframework.aop.Advisor} beans * @see #isEligibleBean */ public List buildAspectJAdvisors() { // aspectBeanNames,缓存 // tips: aspectBeanNames由volatile修饰 // volatile: 保证变量可见性,指令不可重排 List aspectNames = this.aspectBeanNames; // 如缓存存在,则跳过初始化步骤 if (aspectNames == null) { // synchronized, 同步锁 // 锁住当前实例,里面的内容同一时间只会被一个线程执行 synchronized (this) { // 拿到锁后再次获取缓存,避免重复初始化 aspectNames = this.aspectBeanNames; if (aspectNames == null) { List advisors = new ArrayList<>(); aspectNames = new ArrayList<>(); // 获取原始类为Object的bean,即所有bean String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); for (String beanName : beanNames) { // 是否是合适的bean if (!isEligibleBean(beanName)) { continue; } // We must be careful not to instantiate beans eagerly as in this case they // would be cached by the Spring container but would not have been weaved. Class beanType = this.beanFactory.getType(beanName); if (beanType == null) { continue; } // isAspect 是否为@Aspect注解修饰的Bean // 是不是很亲切方法,终于读到它了。这是我们在第二章一开始就提到的方法。 if (this.advisorFactory.isAspect(beanType)) { aspectNames.add(beanName); // 由beanType,beanName组合Metadata,包含了创建Advisor需要的内容 AspectMetadata amd = new AspectMetadata(beanType, beanName); // PerClauseKind.SINGLETON 单例模式 // 由@Aspect中的value参数配置,这个参数Bean的scope 有点类似,用来配置生命 周期,默认都为单例。可配为"每..."的模式 if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { // 生成实例工厂类 MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // 生成Advisors实例 List classAdvisors = this.advisorFactory.getAdvisors(factory); // 如果Aspect Bean是单例,则缓存到advisorsCache if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } // 如果不是,则将工厂缓存到aspectFactoryCache else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } else { // Per target or per this. // bean为单例,@Aspect也要配置为单例 if (this.beanFactory.isSingleton(beanName)) { throw new IllegalArgumentException("Bean with name "" + beanName + "" is a singleton, but aspect instantiation model is not singleton"); } // 跟前一个分支一样,生成Advisors实例,然后将工厂缓存到aspectFactoryCache MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName); this.aspectFactoryCache.put(beanName, factory); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } } this.aspectBeanNames = aspectNames; return advisors; } } } if (aspectNames.isEmpty()) { return Collections.emptyList(); } List advisors = new ArrayList<>(); for (String aspectName : aspectNames) { // 通过aspectName(beanName)获取advisors缓存 List cachedAdvisors = this.advisorsCache.get(aspectName); // 如已存在,则加载advisorsCache if (cachedAdvisors != null) { advisors.addAll(cachedAdvisors); } // 如不存在,则加载factoryCache,再从工厂生成advisors,与上面初始时候的两个分支对应 // ps:这里并没有做factory的空判断... else { MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } return advisors; } ....

由于这段代码比较长,我将过程注释在代码中。其中英文为源码注释。

那么,以上便是通过beanName扫描@Aspect配置并生成Advisor的过程了。其中this.advisorFactory.getAdvisors(factory)是生成Advisor类的具体内容。深挖的话还能再写一篇文章,这里就不细说了。有兴趣的可以自行阅读。

findAdvisorsThatCanApply

现在我们获得了所有的候选Advisor,那么找出和当前Bean匹配的Advisor呢?

/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java
....
/**
 * Search the given candidate Advisors to find all Advisors that
 * can apply to the specified bean.
 * @param candidateAdvisors the candidate Advisors
 * @param beanClass the target"s bean class
 * @param beanName the target"s bean name
 * @return the List of applicable Advisors
 * @see ProxyCreationContext#getCurrentProxiedBeanName()
 */
protected List findAdvisorsThatCanApply(
        List candidateAdvisors, Class beanClass, String beanName) {

    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null);
    }
}
....

一步一步往下探

/org/springframework/aop/support/AopUtils.java
....
/**
 * Determine the sublist of the {@code candidateAdvisors} list
 * that is applicable to the given class.
 * @param candidateAdvisors the Advisors to evaluate
 * @param clazz the target class
 * @return sublist of Advisors that can apply to an object of the given class
 * (may be the incoming List as-is)
 */
public static List findAdvisorsThatCanApply(List candidateAdvisors, Class clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List eligibleAdvisors = new ArrayList<>();
    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) {
            // already processed
            continue;
        }
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    return eligibleAdvisors;
}
....
/**
 * Can the given advisor apply at all on the given class?
 * This is an important test as it can be used to optimize
 * out a advisor for a class.
 * @param advisor the advisor to check
 * @param targetClass class we"re testing
 * @return whether the pointcut can apply on any method
 */
public static boolean canApply(Advisor advisor, Class targetClass) {
    return canApply(advisor, targetClass, false);
}

/**
 * Can the given advisor apply at all on the given class?
 * 

This is an important test as it can be used to optimize out a advisor for a class. * This version also takes into account introductions (for IntroductionAwareMethodMatchers). * @param advisor the advisor to check * @param targetClass class we"re testing * @param hasIntroductions whether or not the advisor chain for this bean includes * any introductions * @return whether the pointcut can apply on any method */ 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; return canApply(pca.getPointcut(), targetClass, hasIntroductions); } else { // It doesn"t have a pointcut so we assume it applies. return true; } } ....

最后定位到canApply(Pointcut pc, Class targetClass, boolean hasIntroductions)方法

/org/springframework/aop/support/AopUtils.java
/**
 * Can the given pointcut apply at all on the given class?
 * 

This is an important test as it can be used to optimize * out a pointcut for a class. * @param pc the static or dynamic pointcut to check * @param targetClass the class to test * @param hasIntroductions whether or not the advisor chain * for this bean includes any introductions * @return whether the pointcut can apply on any method */ 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(); 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> classes = new LinkedHashSet<>(); if (!Proxy.isProxyClass(targetClass)) { classes.add(ClassUtils.getUserClass(targetClass)); } classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(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; }

可以看出判断是否是该bean合适的advisor,是通过advisor.getPointcut().getClassFilter().matches(targetClass)方法来判断的。匹配完class以后下面还有MethodMatcher来匹配method。回想我们在配置pointcut的时候不仅仅有class的规则,也有method的规则。

当然,再深入matches方法进去的话就是pointcut的匹配语法实现了。有兴趣的可以自行阅读。

一些总结

读到这儿,Spring AOP如何扫描@Aspect配置,生成Advisor类,并匹配对应的Bean整个流程已经很清楚了。这里再总结一下:

获取已在BeanFactory的Advisor Bean

获取所有Object Bean,过滤出@Aspect注解修饰的Bean,并生成Advisor

遍历上述获取的所有Advisor,由Advisor的Pointcut ClassFilter匹配合适的Bean

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76783.html

相关文章

  • Spring AOP 源码初窥(二) 从注解开始

    摘要:版本从注解开始由于在本人实际应用中使用的是注解配置,也更倾向于了解的整个实现,而不仅仅是关键实现。于是本篇源码解析,将会从注解开始。那么,便从的源码引用开始吧。的引用先从源码中找有引用到,用来判断是否有该注解的代码。 版本 spring 5.0.8.BUILD-SNAPSHOT aspectjweaver 1.8.13 从注解开始 由于在本人实际应用中使用的是注解配置AOP,也更倾...

    Amio 评论0 收藏0
  • 源码入手,一文带你读懂Spring AOP面向切面编程

    摘要:,,面向切面编程。,切点,切面匹配连接点的点,一般与切点表达式相关,就是切面如何切点。例子中,注解就是切点表达式,匹配对应的连接点,通知,指在切面的某个特定的连接点上执行的动作。,织入,将作用在的过程。因为源码都是英文写的。 之前《零基础带你看Spring源码——IOC控制反转》详细讲了Spring容器的初始化和加载的原理,后面《你真的完全了解Java动态代理吗?看这篇就够了》介绍了下...

    wawor4827 评论0 收藏0
  • Spring AOP 源码初窥(一) 概念

    摘要:而面向切面编程理所当然关注于切面,那么什么是切面可以理解为程序执行时的某个节点,或更具体一点,在某个方法执行之前,执行之后,返回之后等其它节点。术语一个切面,可以理解为一个切面模块,将相关的增强内容写进同一个切面。例如一个负责日志的切面。 AOP是什么 AOP全称 Aspect-Oriented Programming 即面向切面编程。怎么样,是不是感觉很熟悉?对,类似的还有面向过程编...

    CarterLi 评论0 收藏0
  • 慕课网_《Spring入门篇》学习总结

    摘要:入门篇学习总结时间年月日星期三说明本文部分内容均来自慕课网。主要的功能是日志记录,性能统计,安全控制,事务处理,异常处理等等。 《Spring入门篇》学习总结 时间:2017年1月18日星期三说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:https://github.com/zccodere/s...个人学习源码:https://git...

    Ververica 评论0 收藏0
  • 重拾-Spring AOP-自动代理

    摘要:是通过判断当前是否匹配,只有匹配的才会创建代理。实现分析类结构从上图类结构,我们知道其实现与类似都是通过实现接口在完成实例化后进行自动代理处理。 概述 在上一篇 重拾-Spring AOP 中我们会发现 Spring AOP 是通过类 ProxyFactoryBean 创建代理对象,其有个缺陷就是只能代理一个目标对象 bean, 当代理目标类过多时,配置文件臃肿不方便管理维护,因此 S...

    Mr_houzi 评论0 收藏0

发表评论

0条评论

oysun

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<