跟着小马哥学系列之 Spring AOP(Advisor 详解)
学好路更宽,钱多少加班。 ——小马哥
简介
大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在多次学习极客时间《小马哥讲Spring AOP 编程思想》基础上形成的个人一些总结。希望能帮助各位小伙伴, 祝小伙伴早日学有所成。
Advisor
保存 AOP
通知
(在连接点上采取的行动)和决定相对应的通知的过滤器(如切点功能)
。Advisor 接口支持不同类型的通知,比如前置和后置通知,他们不需要使用拦截来实现(在获取拦截器链时会自动适配成 MethodInterceptor 类型)。Advisor 是 Spring AOP 底层使用的重要接口,由它来封装通知和切点。分成两大类IntroductionAdvisor
和PointcutAdvisor
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
又继承了 Advisor
和 IntroductionInfo
,既要实现 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 类型即 DeclareParentsAdvisor
和 InstantiationModelAwarePointcutAdvisor
。用户定义的 advice 到 Spring AOP 底层就会被封装成 Advisor,然后再进行直接或者间接(本身就实现了 MethodInterceptor 接口)适配成 MethodInterceptor 完成通知
跟着小马哥学系列之 Spring AOP(Advisor 详解)相关推荐
- 跟着小马哥学系列之 Spring AOP(Pointcut 组件详解)
学好路更宽,钱多少加班. --小马哥 版本修订 2021.5.19:去除目录 2021.5.21:引用 Spring 官方 Pointcut 概念,修改 Pointcut 功能表述 简介 大家好,我是 ...
- 跟着小马哥学系列之 Spring AOP(AbstractAutoProxyCreator 详解)
学成路更宽,吊打面试官. --小马哥 版本修订 2021.5.19:去除目录 简介 大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了 ...
- 跟着小马哥学系列之 Spring AOP(基于 XML 定义 Advice 源码解析)
学好路更宽,钱多少加班. --小马哥 简介 大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在多次学习极客时间< ...
- 跟着小马哥学系列之 Spring IoC(源码篇:Bean 生命周期)
跟着小马哥学系列之 Spring IoC(源码篇:Bean 生命周期) 简介 Bean 元信息来源 Bean 元信息解析成 BeanDefinition 并注册 BeanDefinition 转变成 ...
- 跟着小马哥学系列之 Spring IoC(源码篇:@Import)
跟着小马哥学系列之 Spring IoC(源码篇:@Import) 简介 @ Import 简介 元信息 元注解 属性 @Import 注解 value 属性取值范围 ImportSelector I ...
- 跟着小马哥学系列之 Spring IoC(进阶篇:Environment)
学成路更宽,吊打面试官. --小马哥 简介 大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在多次学习极客时间< ...
- 跟着小马哥学系列之 Spring IoC(进阶篇:类型转换)
学成路更宽,吊打面试官. --小马哥 简介 大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在多次学习极客时间< ...
- Spring AOP知识详解
本文来详细说下spring中的aop内容 文章目录 Spring AOP概述 Spring AOP一代 Pointcut Jointpoint Advice Advisor 织入 Spring AOP ...
- spring注解:spring aop注解详解
一. AOP的基本概念 Aspect(切面):通常是一个类,里面可以定义切入点和通知 Pointcut(切点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式 JointPoint(连接点) ...
最新文章
- python字典{:4}_Python字典101:详细的视觉介绍
- HDU-2037-今年暑假不AC
- STM32学习及开发笔记八:采用主从计时器实现精确脉冲输出
- Python数据分析:pandas玩转Excel(三)
- 查看linux 系统情况,LINUX 查看当前系统的负载情况
- 为什么说阿里巴巴已进化成为一家世界级的科技公司?
- SVN使用方法及问题解决
- App专项测试测试有哪些?
- 分享几十年来记录下的编程技巧
- 内网通修改积分文件_【页游逆向】4399小游戏积分系统分析及修改积分
- 目标检测的图像特征提取之LBP特征
- NIST计划对量子加密进行众测
- android 圆形进度条设置进度条,Android实现带数字的圆形进度条(自定义进度条)
- 创建FTP站点访问超链接
- 电影下载的TS TC版
- SDH原理--2.SDH信号的帧结构
- 3D动画效果照片墙demo
- java.security.InvalidKeyException:illegal Key Size 报错解决方案
- 哨兵二号数据下载(欧空局)
- 字节带货新大陆,抖音如何做好跨境电商这门生意?