跟着小马哥学系列之 Spring AOP(AdvisorChainFactory 详解)

学成路更宽,吊打面试官。 ——小马哥

版本修订

  • 2021.5.19:去除目录

简介

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

AdvisorChainFactory 功能

AdvisorChainFactory 接口只有唯一一个方法就是 List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) 。根据 javadoc 介绍此方法是根据方法参数 config返回 MethodInterceptor 列表,在 Spring AOP 内部只有一个唯一实现 DefaultAdvisorChainFactory

前置知识

Advised

Advised 功能

从 javadoc 可知: 此接口实现类拥有配置 AOP 代理工厂的能力(AopProxyFactory #createAopProxy 方法参数就是此实现类 ),此配置包含 InterceptorAdviceAdvisor代理接口。从 Spring 获得的任何 AOP 代理都可以转换到这个接口,以允许操作它的 AOP Advice(详情请跟踪 CglibAopProxy 中调用 AopProxyUtils#completeProxiedInterfaces(AdvisedSupport advised)JdkDynamicAopProxy 中调用 AopProxyUtils.completeProxiedInterfaces((AdvisedSupport advised, boolean decoratingProxy) 会发现宝藏)。

Advised 方法简介

  • boolean isFrozen():Advised 配置是否冻结,冻结之后不能更改任何 Advice。
  • boolean isProxyTargetClass():对应 @EnableAspectJAutoProxy 中的 proxyTargetClass 属性
  • Class<?>[] getProxiedInterfaces():返回由AOP代理代理的接口
  • boolean isInterfaceProxied(Class<?> intf):判断给定接口是否被代理
  • void setTargetSource(TargetSource targetSource):更改当前 Advised 对象使用的TargetSource,仅当配置不是 isFrozen 为 true 时有效。
  • TargetSource getTargetSource():获取当前 Advised 对象使用的 TargetSource
  • void setExposeProxy(boolean exposeProxy):对应 @EnableAspectJAutoProxy 中的 exposeProxy 属性
  • boolean isExposeProxy():返回工厂是否应该将代理暴露到 ThreadLocal(AopContext)。如果 Advise 的对象需要在应用 Advice 的情况下调用自身的方法,则有必要公开代理。否则,如果 Advice 的对象对此调用一个方法,则不会应用任何 Advice。获取代理类似于调用getEJBObject()的EJB。
  • void setPreFiltered(boolean preFiltered):设置此代理配置是否经过预过滤,以便仅包含可用的 Advisor (匹配此代理的目标类)。默认设置是 false。如果 Advisor 已经被预先过滤,则将其设置为 true,这意味着在为代理调用构建实际的 Advisor 链时可以跳过 ClassFilter 检查。
  • boolean isPreFiltered():返回此代理配置是否已预过滤,以便仅过滤包含可用的 Advisor (匹配此代理的目标类)。
  • Advisor[] getAdvisors():返回应用于当前代理的 Advisor 列表
  • void addAdvisor(Advisor advisor):在 Advisor 链末尾添加 Advisor
  • void addAdvisor(int pos, Advisor advisor):在 Advisor 链指定的位置添加 Advisor
  • boolean removeAdvisor(Advisor advisor):移除指定 Advisor
  • void removeAdvisor(int index):移除指定位置的 Advisor
  • int indexOf(Advisor advisor):检索 Advisor 在 Advisor 链中的位置
  • boolean replaceAdvisor(Advisor a, Advisor b):替换指定 Advisor
  • void addAdvice(Advice advice):添加 Advice 到 Advice 链末端(根据 Advice类型不同会被包装成 DefaultPointcutAdvisor或者 DefaultIntroductionAdvisor
  • void addAdvice(int pos, Advice advice):在 Advice 链指定的位置添加 Advisor
  • boolean removeAdvice(Advice advice):移除指定 Advice
  • int indexOf(Advice advice):检索 Advice 在 Advice 链中的位置
  • String toProxyConfigString():代理配置的字符串描述

Advisor

Advisor 功能

从 javadoc 可知: 此接口是保存 AOP Advice(在连接点上要采取的动作)的基本接口和决定可用的 Adivce(如切入点)的过滤器。这个接口不是供 Spring 用户使用的,而是为了支持不同类型的 Advice。
Spring AOP 是基于通过方法拦截来传递 Around Advice(所以没有单独有 Around 类型的 Advice),符合 AOP Alliance 拦截 API。Advisor 接口允许支持不同类型的 Advice,比如 before 和 after Advice。

Advisor 方法简介

  • Advice getAdvice():返回切面的 Advice 部分。 Advice 可以是 MethodInterceptorBeforeAdvice, ThrowsAdvice
  • boolean isPerInstance():返回该 Advice 是否与特定的实例相关联(例如,创建混合),或者与从同一 Spring bean 工厂获得的被通知类的所有实例共享。注意,该框架目前没有使用此方法。典型的 Advisor实现总是返回 true。使用单例/原型 bean 定义或适当的编程代理创建来确保 Advisor 拥有正确的生命周期模型。

AdvisorAdapter

AdvisorAdapter功能

从 javadoc 中可知: 此接口是对 Spring AOP 框架进行扩展,以允许处理新的(自定义的) Advisors 和 Advice 类型。
实现对象可以从自定义 Advice 类型创建 AOP Alliance Interceptor,使这些 Advice 类型可以在 Spring AOP 框架中使用,后者在底层使用拦截。
大多数 Spring 用户不需要实现这个接口;只有在需要向 Spring 引入更多 Advisor 或 Advice 类型时才这样做。
总结一句话: 就是对 Advisors 和 Advice 类型扩展,底层是通过拦截器实现,由于 Spring 只支持方法级别的拦截所以拦截器都实现了 MethodInterceptor

AdvisorAdapter 方法简介

  • boolean supportsAdvice(Advice advice):是否支持此 Advice 对象
  • MethodInterceptor getInterceptor(Advisor advisor):返回一个AOP Alliance MethodInterceptor,将给定 Advice 的行为暴露给一个基于拦截的 AOP 框架。不要担心 Advisor 中包含的任何 Pointcut ;AOP 框架将负责检查 Pointcut。

AdvisorAdapterRegistry

AdvisorAdapterRegistry 功能

从 javadoc 中可知: 此接口是注册 AdvisorAdapter(这是一个 SPI 接口,不是由任何 Spring 用户实现的),在 Spring AOP 内部只有一个唯一实现 DefaultAdvisorAdapterRegistry

AdvisorAdapterRegistry 方法简介

  • Advisor wrap(Object advice):把给定的 Adivice 对象包装成 Advisor
  • MethodInterceptor[] getInterceptors(Advisor advisor):把 Advisor 转换成 MethodInterceptor
  • void registerAdvisorAdapter(AdvisorAdapter adapter):注册 AdvisorAdapter (用于扩展 AdvisorAdapter )

Pointcut

Pointcut 功能

从 javadoc 中可知: 此接口是 Spring Pointcut 核心抽象。Pointcut 由一个 ClassFilter 和一个MethodMatcher 组成
总结一句话: Pointcut 要先经过类型匹配上了之后再通过方法进行匹配

Pointcut 方法简介

  • ClassFilter getClassFilter():获取 Pointcut 关联的 ClassFilter 实现
  • MethodMatcher getMethodMatcher():获取 Pointcut 关联的 MethodMatcher 实现

MethodMatcher

MethodMatcher 功能

从 javadoc 中可知: 此接口是 Pointcut 的一部分:用于检查目标方法是否有资格获得 Advice。
MathodMatcher 可以静态地或在运行时(动态地)计算。静态匹配涉及方法和(可能)方法属性。动态匹配还使特定调用的参数可用,以及将先前的 Advice 应用到 Joinpoint的任何效果。
如果实现 isRuntime() 方法返回 false,则可以静态执行求值,并且该方法的所有调用的结果都是相同的,无论它们的参数是什么。这意味着如果 isRuntime()方法返回 false,则 3 参数 matches(Method method, Class<?> targetClass, Object... args) 方法将永远不会被调用。
如果一个实现从它的 2 参数 matches(Method method, Class<?> targetClass)返回 true和它的 isRuntime() 方法返回 true, 3 参数 matches(Method method, Class<?> targetClass, Object... args) 方法将在相关 Advice 的每次潜在执行之前被立即调用,以决定该 Advice 是否应该运行。之前的所有 Adivce (比如拦截器链中的早期拦截器)都将运行,因此它们在参数或 ThreadLocal 状态中产生的任何状态更改都将在计算时可用。
该接口的具体实现通常应该提供 Object.equals(Object) 和 Object. hashcode() 的适当实现,以便允许匹配器在缓存场景中使用——例如,在 CGLIB 生成的代理中。
总结一句话: 如果 2个参数的 boolean matches(Method method, Class<?> targetClass)方法返回 true,isRuntime() 方法也是 true 则会调用 3 个参数 matches(Method method, Class<?> targetClass, Object... args) 方法如果返回 true 再进行 Advice

MethodMatcher 方法简介

  • boolean matches(Method method, Class<?> targetClass):执行静态检查给定的方法是否匹配,如果返回 falseisRuntime()方法返回 false,则不进行运行时检查(即不 matches(java.lang.reflect.Method, Class, Object[]) 调用)
  • boolean isRuntime():这个 MethodMatcher 是动态还是静态
  • boolean matches(Method method, Class<?> targetClass, Object… args):只有上述2个方法都返回 true 才执行。

前置知识大总结

  1. 通过 Pointcut 关联的 ClassFilterMethodMatcher 筛选构成 Joinpoint,然后再基于不同 Advice 类型进行执行动作。
  2. Spring AOP 框架内部有个全局对象 GlobalAdvisorAdapterRegistry,它内部关联一个 AdvisorAdapterRegistry 对象。通过 AdvisorAdapterRegistry#registerAdvisorAdapter 方法注册 AdvisorAdapter。可以通过 Adviced 对象可获得 Advisor 对象或者由 AdvisorAdapterRegistry#wrap() 方法获得 Advisor 对象。通过 AdvisorAdapterRegistry#getInterceptors 方法获取 MethodInterceptor 数组。

AdvisorChainFactory 源码解析

由于 AdvisorChainFactory 只有一个唯一实现 DefaultAdvisorChainFactory 源码分析则是在此类进行。

@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
		Advised config, Method method, @Nullable Class<?> targetClass) {
	// 获取 AdvisorAdapterRegistry
	AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
	// 获取 Advisor 列表
	Advisor[] advisors = config.getAdvisors();
	List<Object> interceptorList = new ArrayList<>(advisors.length);
	Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
	Boolean hasIntroductions = null;
	for (Advisor advisor : advisors) {
		// Advisor 对象是否是 PointcutAdvisor 实例,PointcutAdvisor 具有 Pointcut(过滤) 和 Advisor(动作) 功能 
		if (advisor instanceof PointcutAdvisor) {
			// 获取 PointcutAdvisor .
			PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
			// Advised 配置对象是否设置了预过滤,如果设置了跳过 ClassFilter 检查,如果没有通过则 通过 Pointcut 中关联的 ClassFilter#matches 方法进行检查。
			if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
				// 类型匹配之后,再进行方法匹配
				MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
				boolean match;
				if (mm instanceof IntroductionAwareMethodMatcher) {
					if (hasIntroductions == null) {
						hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
					}
					match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
				}
				else {
					match = mm.matches(method, actualClass);
				}
				// 方法匹配则通过 AdvisorAdapterRegistry 获取 MethodInterceptor 数组				if (match) {
					MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
					// 如果 MethodMatcher 实现对象是动态匹配,则会调用 MethodMatcher#matches(Method method, Class<?> targetClass, Object... args)
					if (mm.isRuntime()) {
						for (MethodInterceptor interceptor : interceptors) {
							interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
						}
					}
					else {
						interceptorList.addAll(Arrays.asList(interceptors));
					}
				}
			}
		}
		// 如果 Advisor 对象是 IntroductionAdvisor 实例,通过类型匹配之后,接口所有的方法都要代理
		else if (advisor instanceof IntroductionAdvisor) {
			IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
			if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
				// 通过 AdvisorAdapterRegistry 获取 MethodInterceptor 数组
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}
		else {
			 /**
         	  * 如果不是 PointcutAdvisor 和 IntroductionAdvisor 即是普通的 Advisor。
         	  * 通过 AdvisorAdapterRegistry 获取 MethodInterceptor 数组
         	  */
			Interceptor[] interceptors = registry.getInterceptors(advisor);
			interceptorList.addAll(Arrays.asList(interceptors));
		}
	}

	return interceptorList;
}


  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿大叔文海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值