盘点 AOP AOP 的拦截与方法调用

首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜

文章合集 : 🎁 juejin.cn/post/694164…

Github : 👉 github.com/black-ant

一 . 前言

之前说了 AOP初始化AOP 代理类的创建 , 这一篇来看一下 AOP 对请求的拦截

AOP 拦截的起点是 DynamicAdvisedInterceptor , 该对象在 CglibAopProxy -> getProxy -> getCallbacks

二 . 拦截的发起

2.1 CglibAopProxy 的拦截开始

1
2
3
4
5
6
7
8
9
JAVA复制代码C- DynamicAdvisedInterceptor # intercept
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

// 仅保留核心方法 , 可以看到这里构建了 CglibMethodInvocation用于调用
Object retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
retVal = processReturnType(proxy, target, method, retVal);
return retVal;

}

2.2 Interceptor 执行 Proceed

上一节创建后 CglibMethodInvocation 后 , 会执行 proceed , 以此调用切面类 , 此处先以 Around 为例 , 后面再看一下其他的几种调用

Step 1 : Proceed 调用逻辑 (ReflectiveMethodInvocation)

主要是调用父类的逻辑 CglibMethodInvocation extends ReflectiveMethodInvocation 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
java复制代码public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}

// 2.2.1 获取具体的 Advice , 此处通过自增完成拦截链的切换
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// 动态方法匹配器-> Pro22101
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
} else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
return proceed();
}
}else {
// 2.2.2 调用 Advice 的 invoke 方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}

// Pro22101 : InterceptorAndDynamicMethodMatcher 的作用
class InterceptorAndDynamicMethodMatcher {
// 方法拦截器
final MethodInterceptor interceptor;
// 方法匹配器
final MethodMatcher methodMatcher;
}

MethodMatcher 对象体系 :

System-MethodMatcher.png

Step 2 : ExposeInvocationInterceptor

这里是通过 ExposeInvocationInterceptor(CglibMethodInvocation) 调用 , 该拦截器的作用为 将当前MethodInvocation公开为本地线程对象的拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码private static final ThreadLocal<MethodInvocation> invocation =new NamedThreadLocal<>("Current AOP method invocation");

public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get();
// 线程设置
invocation.set(mi);
try {
return mi.proceed();
}
finally {
invocation.set(oldInvocation);
}
}

Step 3 : 调用不同的拦截链

从 2.2 步骤中 , 会通过自增的方式迭代拦截器 :
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

image.png

PS : 通常链式结构是递归执行的 , 通常最先执行的在列表最后

1
java复制代码// 调用对应的 xxxAdviceInterceptor -> [Pro25001]

[Pro25001] : Advice 拦截链的调用

1
2
3
4
java复制代码// 此处会对不同的 Advice 进行处理 , 核心带入如下 : 
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

从 interceptorsAndDynamicMethodMatchers 可以看到 , 如果存在 Advice 就会存在相关对象

Step 4 : 反射到 Advice Method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
java复制代码protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
if (this.aspectJAdviceMethod.getParameterCount() == 0) {
actualArgs = null;
}
try {
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
// 调用实际业务方法
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
} catch (IllegalArgumentException ex) {
throw new AopInvocationException("Mismatch on arguments to advice method [" +
this.aspectJAdviceMethod + "]; pointcut expression [" +
this.pointcut.getPointcutExpression() + "]", ex);
} catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}



// 拦截适配器用于把通知转换为拦截器

// 主要的适配器 :
C- MethodBeforeAdviceAdapter


// 补充 invokeAdviceMethodWithGivenArgs 带参数访问
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
if (this.aspectJAdviceMethod.getParameterCount() == 0) {
actualArgs = null;
}
try {
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
// TODO AopUtils.invokeJoinpointUsingReflection
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}
}


PS: 至此就完成了 Advice 方法的调用

2.3 补充其他通知的调用方式

AspectJAfterAdvice 的调用

1
2
3
4
5
6
7
8
9
10
java复制代码
public Object invoke(MethodInvocation mi) throws Throwable {
try {
// Step 1 : 链式调用走流程
return mi.proceed();
} finally {
// Step 2 : finally 完成最终调用
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
}

AfterReturningAdviceInterceptor 的调用

1
2
3
4
5
6
7
java复制代码public Object invoke(MethodInvocation mi) throws Throwable {
// Step 1 : 链式调用走流程
Object retVal = mi.proceed();
// Step 2 : 调用 afterReturning
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}

三 . 方法的调用

以 Around 为例 , 当执行了 ProceedingJoinPoint.proceed() 方法后 , 即开始了实际方法的调用

  • Step 1 : 切面调用 pj.proceed()
  • Step 2 : MethodInvocationProceedingJoinPoint 发起 proceed 操作
  • Step 3 : 循环完成 , 发起方法调用
  • Step 4 : 实际方法调用

3.1 MethodInvocationProceedingJoinPoint 发起 proceed 操作

PS : 此处还没有完成 , 这是构建了一个新的对象 , 从上一个链表图中可以看到 , 还要执行后续的 Before Advice

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
JAVA复制代码public Object proceed() throws Throwable {
return this.methodInvocation.invocableClone().proceed();
}

// PS : invocableClone 的作用
// 解答 : 此处创建了一个浅克隆 , 用于构建一个独立的拦截器 , 并且用于后续索引 , 但是其中的引用被保持
public MethodInvocation invocableClone() {
Object[] cloneArguments = this.arguments;
if (this.arguments.length > 0) {
// 虽然没有传入参数, 但是实际上构建函数中已经传入了
cloneArguments = this.arguments.clone();
}
return invocableClone(cloneArguments);
}

// C- MethodInvocationProceedingJoinPoint
public MethodInvocation invocableClone(Object... arguments) {
// 强制初始化用户属性Map,以便在克隆中有一个共享的Map引用
if (this.userAttributes == null) {
this.userAttributes = new HashMap<>();
}

// 创建MethodInvocation克隆对象
try {
ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation) clone();
clone.arguments = arguments;
return clone;
} catch (CloneNotSupportedException ex) {
throw new IllegalStateException(...);
}
}

3.2 Interceptor 循环完成 , 发起方法调用

补充 ReflectiveMethodInvocation 对象结构 >>

1
2
3
4
5
java复制代码- ReflectiveMethodInvocation : 基于反射的方式,代理方法调用实现类。
I- ProxyMethodInvocation : 代理方法调用接口
M- #proceed() : 执行方法。基于递归的方式,调用每个拦截器链中的拦截器,最后调用真正的方法。
M- #invokeJoinpoint() : 执行真正的方法,即切点的方法。
M- CglibMethodInvocation : 基于CGLIB 的方式,进一步优化调用的实现类。

当循环迭代完成后(currentInterceptorIndex匹配完成) :

1
2
3
4
5
6
7
java复制代码public Object proceed() throws Throwable {
// 我们从一个索引-1开始,并提前递增 , 当全部递增完成后 , 意味着切面全部完成
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// ..........
}

3.3 发起实际方法调用

1
2
3
4
5
6
7
8
9
10
11
java复制代码// Step 3 : 实际方法调用 (2.1 中初始化了该属性)
private final MethodProxy methodProxy;

// 代理方法
protected Object invokeJoinpoint() throws Throwable {
if (this.methodProxy != null) {
return this.methodProxy.invoke(this.target, this.arguments);
} else {
return super.invokeJoinpoint();
}
}

3.4 补充 : 带参数的请求

在 MethodInvocationProceedingJoinPoint 中 ,存在一个带参数的 proceed 方法 , 用于构建带参数的 clone 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码// 除了无参的请求 , 实际上还有个带参的请求 , 他们的请求方式是不一样的
Object proceed(Object[] args)

public Object proceed(Object[] arguments) throws Throwable {

// 首先会校验参数是否存在和长度是否一致 , 这里省略
this.methodInvocation.setArguments(arguments);
return this.methodInvocation.invocableClone(arguments).proceed();
}

// 回顾clone 方法 , 有个变长参数
public MethodInvocation invocableClone(Object... arguments) {
//.............
}

// 这里和上面一样最终是 clone 了一个 ReflectiveMethodInvocation 出来 , 但是为其设置了独立的参数

直到这里 , AOP 的方法调用就完全完成了 >>

四 . 补充 AOP 的拦截链构建

这里补充看一下 AOP 链的调用逻辑

C- AdvisorChainFactory : 通知链工厂

C- DefaultAdvisorChainFactory : 实现类

4.1 DynamicAdvisedInterceptor # intercept 发起拦截链构建

1
2
3
4
java复制代码public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//... 省略其他的逻辑
List<?> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
}

4.2 从缓存中获取拦截器

这里会优先从缓存中获取 , 缓存没有会先创建 ,再放入缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码// 缓存集合
private transient Map<MethodCacheKey, List<Object>> methodCache;

C- AdvisedSupport # getInterceptorsAndDynamicInterceptionAdvice
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
// 缓存没有则直接创建 , 并且放入缓存
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}

4.3 构建通知链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
java复制代码C- DefaultAdvisorChainFactory
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {

// 注册Advisor适配器的接口 , 该接口是一个 SPI 接口
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
// 从 Advised 中获取通知者
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) {
// 如果为切点则执行
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
// 校验是否匹配目标类
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);
}
if (match) {
// 通过切点获取拦截器
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) {
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
// IntroductionAdvisor 通过AOP通知实现额外的接口
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
// org.springframework.aop.interceptor.ExposeInvocationInterceptor
return interceptorList;
}

Advisor 体系结构 :
Advisor-system.png

总结

到了这一篇 AOP 的主要逻辑就全部完成了 , 后续准备说说AOP 的性能分析以及补充知识点 , 等全部完成后 , 再对 AOP 逻辑进行一遍打磨

现阶段程度只是读懂了代码 , 只能看懂为什么这么用 . 等打磨的时候 ,期望能从中学到一些代码的设计精髓 , 以及写一套出来

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%