【Spring源码】20.AOP之切面的收集

image

1. 前言

我们知道aop实现的原理肯定是基于jdk动态代理和cglib代理,经过生成代理对象,对命中切面的方法进行增强。并将代理对象存放到ioc容器中,在其他对象需要的时候注入进去, 那么肯定 需要通过解析类 去解析 并收集 Advisor对象集合 : 需要增强的目标PointCut 以及 具体增强的逻辑Advice。 如果这个类是可以找到增强对象的,那么就应该生成代理。

2. 问题

2.1 生成的代理对象需要什么?

首先 需要进行 切面的收集和匹配

然后 把匹配到的切面 放入 生成的代理对象里,以便后续调用代理方法 来 决定调用哪个切面 来增强。

我们来看下wrapIfNecessary方法里是如何 进行切面的收集和匹配的。

image

3. Advisor增强对象的收集和匹配

3.1 Advisor增强对象的收集

该方法会先收集适合所有的Advisor增强对象,并过滤出匹配当前bean的Advisor对象

image

getAdvicesAndAdvisorsForBean

image

收集及匹配全在这

image

先看收集所有的切面findCandidateAdvisors()

这个方法是被他的子类重写并调用的,看子类AnnotationAwareAspectJAutoProxyCreator的该方法

image
image

spring自带的advisors可以不看,我们直接看我们自己写的@Aspect类

image

这里有两个缓存

image

buildAspectJAdvisors方法里会先判断缓存

image

第一次没有,肯定先解析再获取,我们看解析的逻辑

可以看到会遍历spring容器里的每一个beanName,然后根据beanName获取class,如果类上有Aspect注解,说明是需要解析的

将 beanFactory, beanName 封装成一个获取Advisor的工厂,然后调用 this.advisorFactory.getAdvisors(factory) 去获取advisors集合

image

3.1.1. 获取没有 @PointCut注解的方法集合

取出从获取advisors工厂里取出beanClass, 去遍历所有没有@PointCut注解的方法,包含什么注解也没有的方法

image

具体怎么获取,也很简单,遍历每一个方法,如果没有@Pointcut 注解就可以

image

3.1.2. 对要解析的方法集合排序

随后会对 没有@PointCut注解的方法集合进行排序, 按照这个顺序排序 : Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class

image

METHOD_COMPARATOR 比较器 :

image

3.1.3. 遍历没有@PointCut注解的方法的集合,开始创建Advisor对象

这里就开始要根据方法上面 aop相关的注解 来常见Advisor对象。

首先需要明确一点,Advisor定义的 是 增强的目标 与 增强的逻辑,

  1. 增强的目标 则是Pointcut对象定义的
  2. 增强的逻辑 则是Advice对象定义的

所以,Advisor 必须要包含Pointcut对象和 Advice对象。

image

3.1.3.1. Pointcut接口介绍

基于表达式的PointCut实现类是 AspectJExpressionPointcut类,它除了多持有一个表达式外,还会实现的 Pointcut接口。

 public interface Pointcut {

   ClassFilter getClassFilter();

   MethodMatcher getMethodMatcher();

   Pointcut TRUE = TruePointcut.INSTANCE;
}

需要重写getClassFilter()来提供ClassFilter, ClassFilter是一个接口,重写matches方法,来定义是否与类匹配

@FunctionalInterface
public interface ClassFilter {
        // 定义是否与类匹配
   boolean matches(Class<?> clazz);

   ClassFilter TRUE = TrueClassFilter.INSTANCE;
}

重写 getMethodMatcher()方法 来提供MethodMatcher 对象, MethodMatcher也是一个接口

public interface MethodMatcher {
  // 定义是否与类匹配
   boolean matches(Method method, Class<?> targetClass);

   boolean isRuntime();
     // 定义是否与方法匹配
   boolean matches(Method method, Class<?> targetClass, Object... args);

   MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

这里AspectJExpressionPointcut类,自己实现了ClassFilter接口,和IntroductionAwareMethodMatcher接口,可以看到实现至Pointcut接口接口的两个方法 返回的都是自身。

image

重写自ClassFilter接口,和IntroductionAwareMethodMatcher接口 对应的matches方法。
image

这里先这样,后面匹配的时候 再看这两个方法里具体的匹配逻辑。

3.1.3.2. PointCut对象的创建

进入 getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) 方法

image

进入 getPointcut方法,这里先获取Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 这几个注解,匹配到就返回

不可能获取到@Pointcut注解
其实这里是不可能获取到 @Pointcut ,因为前面 遍历的整个方法的集合 就已经是排除掉了 @Pointcut注解的方法

获取需要遍历的整个方法集合代码                 ![image](https://upload-images.jianshu.io/upload_images/23353704-e4ef94bec8d68a4a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

getPointcut方法

注解获取到Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class注解里的其中任意一个,获取注解里的pointcutExpression属性,以及目标类的class对象,设置到创建的AspectJExpressionPointcut对象里,并返回

image

到这里pointCut对象已经创建完成,其实并不会去 扫描有@PointCut注解的方法,只是通过Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class里的pointcut属性,去指向@PointCut注解的方法,到时候可以 根据这个 去找 @PointCut注解的方法,再去找@PointCut里面具体配置的表达式,明确切点。

3.1.3.3. Advice对象的创建

代码回到入 getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) 方法。

会new出InstantiationModelAwarePointcutAdvisorImpl对象, 传入AspectJExpressionPointcut对象到构造方法里

image

InstantiationModelAwarePointcutAdvisorImpl构造方法

会先存PointCut对象以及原生Method对象,再在instantiateAdvice方法里创建Advice对象

image

instantiateAdvice方法里创建Advice对象

image

首先从原生method方法,取出 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 这五个注解中的一个(一个方法只能取到其中一个注解,一般只会配一个)

image

再判断注解,根据注解的不同,创建的Advice对象也不同

image

可以看到注解与创建的AspectJAroundAdvice类有如下对应关系

注解 Aavice实现类
@Around AspectJAroundAdvice
@Before AspectJMethodBeforeAdvice
@After AspectJAfterAdvice
@AfterReturning AspectJAfterReturningAdvice
@AfterThrowing AspectJAfterThrowingAdvice
MethodInterceptor接口

其中AspectJAroundAdvice,AspectJMethodBeforeAdvice,AspectJAfterAdvice这三个类是实现MethodInterceptor接口的

实现的invoke方法 持有 整个aop切面调用链上下文对象,以完成 切面调用链的回调,继续调用剩下的的 切面。

@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
   Object invoke(MethodInvocation invocation) throws Throwable;
}
public class AspectJAfterAdvice extends AbstractAspectJAdvice
      implements MethodInterceptor, AfterAdvice, Serializable {
   @Override
   public Object invoke(MethodInvocation mi) throws Throwable {
      try {
         return mi.proceed();
      }
      finally {
         invokeAdviceMethod(getJoinPointMatch(), null, null);
      }
   }
}
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
    public Object invoke(){
    // 继承的父类 AbstractAspectJAdvice的invoke
    }
    @Override
    public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
}

public class AspectJAfterAdvice extends AbstractAspectJAdvice
        implements MethodInterceptor, AfterAdvice, Serializable {
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        try {
            return mi.proceed();
        }
        finally {
            invokeAdviceMethod(getJoinPointMatch(), null, null);
        }
    }
}

而AspectJAfterReturningAdvice,AspectJAfterThrowingAdvice这两个切面没有,到时候aop调用的时候,会对这两个切面类进行 MethodInterceptor类型适配,以保证 调用和调用链 传递逻辑的 统一。

最终返回 各自的切面对象。

image

Advisor对象创建完成,存入缓存

直到所有方法都扫描完成,对应的Advisor对象 也创建成功,都存入缓存: beanName -> 对应类解析出来的 advisors

image

至此, 当容器内所有的beanName都遍历完成,对应的class 通过解析、创建出来的Advisor对象全部 收集完毕。

image
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,881评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,052评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,598评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,407评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,823评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,872评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,037评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,778评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,505评论 1 247
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,745评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,233评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,231评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,141评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,939评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,954评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,784评论 2 275

推荐阅读更多精彩内容