学好路更宽,钱多少加班。 ——小马哥

简介

大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在多次学习极客时间《小马哥讲Spring AOP 编程思想》基础上形成的个人一些总结。希望能帮助各位小伙伴, 祝小伙伴早日学有所成。

Advisor

保存 AOP 通知(在连接点上采取的行动)和决定相对应的通知的过滤器(如切点功能) 。Advisor 接口支持不同类型的通知,比如前置和后置通知,他们不需要使用拦截来实现(在获取拦截器链时会自动适配成 MethodInterceptor 类型)。Advisor 是 Spring AOP 底层使用的重要接口,由它来封装通知和切点。分成两大类 IntroductionAdvisorPointcutAdvisor

IntroductionAdvisor

为执行一个或多个 AOP 引入(Introduction)的 Advisor 提供的超接口。这个接口不能直接实现;子接口必须提供实现引入(Introduction)的通知类型。引入(Introduction)是通过 AOP 通知实现额外的接口(不是由目标实现的)。引入(Introduction)对应 AspectJ @DeclareParents 注解,引入是对目标类进行实现额外的接口来达到目的。所以继承了 IntroductionInfo 接口,通过 getInterfaces 获取需要引入的接口。通过自身提供的 ClassFilter 来决定哪些类需要引入,通过 validateInterfaces() 方法来验证在添加一个 IntroductionAdvisor 之前通知接口是否实现了引入通知

public interface IntroductionAdvisor extends Advisor, IntroductionInfo {/*** 返回 ClassFilter,该 ClassFilter 确定该引入应该应用于哪个目标类。*/ClassFilter getClassFilter();/*** * 验证在添加一个 IntroductionAdvisor 之前通知接口是否实现了引入通知*/void validateInterfaces() throws IllegalArgumentException;}

DeclareParentsAdvisor

引入 advisor 委托给给定的对象。为 @DeclareParent 注解实现 AspectJ 注解风格的行为。


从类图上看 DeclareParentsAdvisor 实现了 IntroductionAdvisor ,而 IntroductionAdvisor 又继承了 AdvisorIntroductionInfo,既要实现 IntroductionAdvisor#getClassFilter()、IntroductionAdvisor#validateInterfaces() 也要实现 Advisor# getAdvice() 和 Advisor# etAdvice() 以及 IntroductionInfo#getInterfaces() 方法。

构造器

/*** 通过 DeclareParents 字段创建一个 advisor 对象。* * @param interfaceType 定义的 Introduction 字段(对应被标注 @DeclareParents 注解的字段)* @param typePattern  introduction 类型限制模式(对应 @DeclareParents 注解中 value 属性)* @param defaultImpl  默认实现类(对应 @DeclareParents 注解中 defaultImpl 属性)*/
public DeclareParentsAdvisor(Class<?> interfaceType, String typePattern, Class<?> defaultImpl) {// DelegatePerTargetObjectIntroductionInterceptor 间接实现了 MethodInterceptor 接口,// 而 MethodInterceptor 接口又继承了 Advice,所以 DelegatePerTargetObjectIntroductionInterceptor 也是 Advice 子类this(interfaceType, typePattern, new DelegatePerTargetObjectIntroductionInterceptor(defaultImpl, interfaceType));
}/*** 私有构造函数在基于impl的委托和基于引用的委托之间共享公共代码* (由于使用了final字段,不能使用init()等方法共享公共代码)。* * @param interfaceType 定义的 Introduction 字段(对应被标注 @DeclareParents 注解的字段)* @param typePattern   introduction 类型限制模式(对应 @DeclareParents 注解中 value 属性)* @param interceptor   代理通知,如:IntroductionInterceptor*/
private DeclareParentsAdvisor(Class<?> interfaceType, String typePattern, IntroductionInterceptor interceptor) {// 把 DelegatePerTargetObjectIntroductionInterceptor 对象赋值给 advice 字段this.advice = interceptor;this.introducedInterface = interfaceType;// 根据 @DeclareParents 注解中 value 属性值来过滤ClassFilter typePatternFilter = new TypePatternClassFilter(typePattern);// 排除自己引入自己ClassFilter exclusion = (clazz -> !this.introducedInterface.isAssignableFrom(clazz));// 把上述2个 ClassFilter 合并成一个 ClassFilterthis.typePatternClassFilter = ClassFilters.intersection(typePatternFilter, exclusion);
}

使用时机

在代理对象反生方法执行的时候,会通过拦截链依次执行里面的通知。具体详情则在 ReflectiveMethodInvocation#proceed() 方法中 ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this) 就会调用 DelegatePerTargetObjectIntroductionInterceptor#invoke()方法,如果是执行的是引入接口的方法,则通过反射调用。

PointcutAdvisor

由切点来驱动所有的 advisor 的超接口。这几乎涵盖了所有的 advisor (除了方法级匹配不适用的引入 advisor )

public interface PointcutAdvisor extends Advisor {/*** 获取驱动该 adviosr 的切点。*/Pointcut getPointcut();}

AbstractPointcutAdvisor

PointcutAdvisor 实现的抽象基类。可以被子类化,以返回特定的切点/通知或可自由配置的切点/通知。不仅实现了 PointcutAdvisor 还实现了 Ordered,因为通知是有顺序的所以 Advisor 也要维持这个顺序。

@Override
public int getOrder() {// 是否指定顺序if (this.order != null) {return this.order;}// 获取通知Advice advice = getAdvice();// 通知是否实现了 Ordered 接口if (advice instanceof Ordered) {return ((Ordered) advice).getOrder();}// 最低优先级return Ordered.LOWEST_PRECEDENCE;
}

AbstractGenericPointcutAdvisor

抽象通用的 PointcutAdvisor 抽象类,有 setAdvice() 方法允许配置任何通知。

/*** 指定该 Advisor 应该应用的通知*/
public void setAdvice(Advice advice) {this.advice = advice;
}

DefaultPointcutAdvisor

方便的切点驱动的 Advisor 实现。这是最常用的 Advisor 实现。除了引入(Introduction)之外,它可以用于任何切点和通知类型。通常不需要继承这个类,也不需要实现自定义 Advisor。AbstractGenericPointcutAdvisor 子类,通过构造器传入通知,调用父类的 setAdvice() 方法。

InstantiationModelAwarePointcutAdvisor

由 Spring AOP advisor 实现的接口,它包装了可能有延迟初始化策略的 AspectJ 切面。

public interface InstantiationModelAwarePointcutAdvisor extends PointcutAdvisor {/*** 返回该 advisor 关联的 advice 是否是延迟初始化 */boolean isLazy();/*** 返回该 adivisor 关联的 advice 是否已经实例化了 */boolean isAdviceInstantiated();}

InstantiationModelAwarePointcutAdvisorImpl

AspectJPointcutAdvisor 的内部实现。请注意,对于每个目标方法,都有一个该 advisor 的实例(在整合 AspectJ 注解时,每个被 advice 注解标注的方法都是一个 InstantiationModelAwarePointcutAdvisorImpl 实例)。该类实现了不仅实现了 InstantiationModelAwarePointcutAdvisor 还实现了 AspectJPrecedenceInformation(同一个 aspect 排序规则)。

重要方法解读

构造器


public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {// 获得 AspectJ 支持的表达式切点this.declaredPointcut = declaredPointcut;// 获得 AspectJ 通知方法所在的类(即标注 @Apsect 注解的类)this.declaringClass = aspectJAdviceMethod.getDeclaringClass();// 获得 AspectJ 通知方法名称 this.methodName = aspectJAdviceMethod.getName();// 获得 AspectJ 通知方法参数类型列表this.parameterTypes = aspectJAdviceMethod.getParameterTypes();// 获得 AspectJ 通知方法this.aspectJAdviceMethod = aspectJAdviceMethod;// 获得 AspectJAdvisorFactorythis.aspectJAdvisorFactory = aspectJAdvisorFactory;// 获得 AspectJ 切面实例工厂(应为通知是在切面类一个方法,通过 Method#invoke() 方法要传入方法所在的对象)this.aspectInstanceFactory = aspectInstanceFactory;// 同一个切面,通知的顺序this.declarationOrder = declarationOrder;// 切面名称this.aspectName = aspectName;// 切面是否具有延迟初始化(@Aspect 注解中 value 属性不指定默认是单例)if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {// 把定义的 AspectJ 表达式切点与切面实例化工厂关联的切面中的切点做并集Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);// 把定义的切点和并集的切点封装在同一个对象this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);// 通知是延迟初始化this.lazy = true;}else {// 切面是单例this.pointcut = this.declaredPointcut;// 通知不延迟初始化this.lazy = false;// 实例化通知this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);}
}

getAdvice()

内部维护一个变量,如果一个实例化过后就不需要再实例化了。如果是延迟实例化的通知是有作用的,因为在构造 InstantiationModelAwarePointcutAdvisorImpl 时,如果不是延迟实例化的切面直接就调用 instantiateAdvice() 方法进行实例化了。


@Override
public synchronized Advice getAdvice() {if (this.instantiatedAdvice == null) {this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);}return this.instantiatedAdvice;
}

instantiateAdvice()

通过 AspectJAdvisorFactory#getAdvice()方法获得通知对象,获取通知对象的详细过程请阅读 跟着小马哥学系列之 Spring AOP(AspectJAdvisorFactory 详解)

private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {// 通过构造器传入的 AspectJAdvisorFactory#getAdvice 方法获取通知。Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,this.aspectInstanceFactory, this.declarationOrder, this.aspectName);return (advice != null ? advice : EMPTY_ADVICE);
}

总结

因为日常开发中主要使用的是 AspectJ 注解或者 AspectJ 语法,所以主要介绍了 Spring AOP 整合 AspectJ 中使用到的 Advisor 类型即 DeclareParentsAdvisorInstantiationModelAwarePointcutAdvisor 。用户定义的 advice 到 Spring AOP 底层就会被封装成 Advisor,然后再进行直接或者间接(本身就实现了 MethodInterceptor 接口)适配成 MethodInterceptor 完成通知

跟着小马哥学系列之 Spring AOP(Advisor 详解)相关推荐

  1. 跟着小马哥学系列之 Spring AOP(Pointcut 组件详解)

    学好路更宽,钱多少加班. --小马哥 版本修订 2021.5.19:去除目录 2021.5.21:引用 Spring 官方 Pointcut 概念,修改 Pointcut 功能表述 简介 大家好,我是 ...

  2. 跟着小马哥学系列之 Spring AOP(AbstractAutoProxyCreator 详解)

    学成路更宽,吊打面试官. --小马哥 版本修订 2021.5.19:去除目录 简介 大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了 ...

  3. 跟着小马哥学系列之 Spring AOP(基于 XML 定义 Advice 源码解析)

    学好路更宽,钱多少加班. --小马哥 简介 大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在多次学习极客时间< ...

  4. 跟着小马哥学系列之 Spring IoC(源码篇:Bean 生命周期)

    跟着小马哥学系列之 Spring IoC(源码篇:Bean 生命周期) 简介 Bean 元信息来源 Bean 元信息解析成 BeanDefinition 并注册 BeanDefinition 转变成 ...

  5. 跟着小马哥学系列之 Spring IoC(源码篇:@Import)

    跟着小马哥学系列之 Spring IoC(源码篇:@Import) 简介 @ Import 简介 元信息 元注解 属性 @Import 注解 value 属性取值范围 ImportSelector I ...

  6. 跟着小马哥学系列之 Spring IoC(进阶篇:Environment)

    学成路更宽,吊打面试官. --小马哥 简介 大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在多次学习极客时间< ...

  7. 跟着小马哥学系列之 Spring IoC(进阶篇:类型转换)

    学成路更宽,吊打面试官. --小马哥 简介 大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在多次学习极客时间< ...

  8. Spring AOP知识详解

    本文来详细说下spring中的aop内容 文章目录 Spring AOP概述 Spring AOP一代 Pointcut Jointpoint Advice Advisor 织入 Spring AOP ...

  9. spring注解:spring aop注解详解

    一. AOP的基本概念 Aspect(切面):通常是一个类,里面可以定义切入点和通知 Pointcut(切点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式 JointPoint(连接点) ...

最新文章

  1. python字典{:4}_Python字典101:详细的视觉介绍
  2. HDU-2037-今年暑假不AC
  3. STM32学习及开发笔记八:采用主从计时器实现精确脉冲输出
  4. Python数据分析:pandas玩转Excel(三)
  5. 查看linux 系统情况,LINUX 查看当前系统的负载情况
  6. 为什么说阿里巴巴已进化成为一家世界级的科技公司?
  7. SVN使用方法及问题解决
  8. App专项测试测试有哪些?
  9. 分享几十年来记录下的编程技巧
  10. 内网通修改积分文件_【页游逆向】4399小游戏积分系统分析及修改积分
  11. 目标检测的图像特征提取之LBP特征
  12. NIST计划对量子加密进行众测
  13. android 圆形进度条设置进度条,Android实现带数字的圆形进度条(自定义进度条)
  14. 创建FTP站点访问超链接
  15. 电影下载的TS TC版
  16. SDH原理--2.SDH信号的帧结构
  17. 3D动画效果照片墙demo
  18. java.security.InvalidKeyException:illegal Key Size 报错解决方案
  19. 哨兵二号数据下载(欧空局)
  20. 字节带货新大陆,抖音如何做好跨境电商这门生意?

热门文章

  1. 如何用crt连接hcl模拟器
  2. 安全漏洞管理解决方案,代码安全解决,网络安全解决
  3. (转)清华“差生”10年奋斗经历
  4. Java使用executeUpdate()导致程序异常
  5. 3. 用户/管理员注册登录 - 如何使用个人Google账号来登录注册门户网站
  6. 酒精和肠内外健康:有帮助还是有害?
  7. 关于JediTerm
  8. 把 Excel 透视表搬到 WEB 上
  9. html中container的代码,代码container什么意思
  10. 基于Java+spring boot的旅游景区小程序的设计与实现