前言
根据上篇文章的介绍,当我们创建好AOP的代理对象后,当增强的方法被调用后,就会走到DynamicAdvisedInterceptor的intercept方法的逻辑,也正是在此Spring会组装好对调用方法的拦截器链,然后通过责任链模式执行,从而实现拦截目标方法的逻辑。
AOP执行的三大步
总览AOP执行流程
首先,一起看看代理拦截的执行逻辑吧
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {//exposeProxy:暴露代理对象,使用了代理对象就有了增强功能
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
oldProxy = AopContext.setCurrentProxy(proxy);//使用ThreadLocal线程共享这个代理对象
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
//责任链模式在执行目标方法前后执行其他的通知方法
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);//通知方法转拦截器链
Object retVal;
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 直接反射执行目标方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
//创建一个方法执行的东西(拦截器链在此执行)
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}
其实,总体看下来,这个方法主要干了三件事:
- 根据exposeProxy的值来决定是否暴露当前的代理对象
- 将当前执行方法的增强器转换为拦截器链
- 责任链模式执行拦截器链逻辑
既然干了哪三件事情我们已经知道了,那我们接下来就挨个的来看Spring是如何干这三件事的:
第一步:是否暴露代理对象
在开启AOP的注解模式的@EnableAspectJAutoProxy注解中有一个exposeProxy属性,决定了是否暴露生成的代理对象,默认是false的,即不暴露。当我们设置exposeProxy=true后,就会触发AopContext.setCurrentProxy(proxy)的逻辑,我们看一下AopContext的代码:
public final class AopContext {
private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");
private AopContext() {
}
public static Object currentProxy() throws IllegalStateException {
Object proxy = currentProxy.get();
if (proxy == null) {
throw new IllegalStateException(
"Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available, and " +
"ensure that AopContext.currentProxy() is invoked in the same thread as the AOP invocation context.");
}
return proxy;
}
@Nullable
static Object setCurrentProxy(@Nullable Object proxy) {
Object old = currentProxy.get();
if (proxy != null) {
currentProxy.set(proxy);
}
else {
currentProxy.remove();
}
return old;
}
}
主要是使用了ThreadLocal来存储了生成的代理对象,这样我们就可以在当前线程中拿到这个线程对象进行其他操作了。有人会问:有什么实际的应用吗?还真有,其实这是我们后面会谈的Spring的事务也是基于AOP这一套的代理拦截模式的,我们知道Spring的事务如果方法内调用就会导致事务失效,这也很好理解,因为非事务方法内部调用事务方法时,只会直接反射调用执行非事务方法的逻辑,执行到事务方法时是不会再走代理拦截的逻辑了,那我们如何让他也走代理拦截的逻辑呢,那就需要通过代理类来调用事务方法才行,那这个代理类我们就可以在AopContext中获取,即拿到当前暴露的代理对象。
第二步:增强器转换为拦截器链
我们需要将当前执行方法的增强器Advice转换为拦截器(MethodInterceptor)链,因为最终的执行是通过责任链的模式来进行拦截执行的,所以首先需要将拦截器链准备好,我们来看下整个转换为拦截器的整个流程(如下图):
Advice转换流程.png
从上图中,我们可以看到整个的转换流程为:
先是拿到所有的Advisor,然后更具Advisor的类型分别来判断这个Advisor是否能拦截当前执行的方法,上文中介绍Advisor的时候,我们知道Spring中提供了两个子接口,分别是PointcutAdvisor和IntroductionAdvisor,两者的区别我们也知道:PointcutAdvisor是方法级别的切入,所以需要先验证类过滤器然后再验证方法匹配器,都通过了再进行转拦截器;而IntroductionAdvisor是类级别的切入,所以我们只需要验证类过滤器通过就可以进行转拦截器了。
Spring提供了DefaultAdvisorAdapterRegistry这个类来帮助我们将Advice转换为拦截器,而所谓的拦截器就是Spring家定义的MethodInterceptor接口,在Spring中Advice并不是都是MethodInterceptor类型的,AOP中的AspectJMethodBeforeAdvice和AspectJAfterReturningAdvice就没有直接实现MethodInterceptor,所以在转换的时候,提供了两个适配器来适配这些不是MethodInterceptor类型的Advice。
我相信很多小伙伴都这里都会问:为什么有的Advice不直接实现MethodInterceptor呢?如果我们自己扩展一个Advice,那我肯定直接实现MethodInterceptor啊,这多方便,而Spring自己却有的需要进行适配器进行适配呢?这个问题得问Spring的开发人员了,两种方式都可以实现,获取只是Spring开发人员告诉你这里可以使用适配器模式吧哈哈哈,也说不定,后面版本就把它替换了呢,咱不纠结这个小问题。
构建责任链模式
有了拦截链集合,然后就是需要构建出责任链的调用了。
责任链调用的核心是在构建责任链处理器,这个处理器控制了整个责任链的流转调用,代码如下:
public Object proceed() throws Throwable {
//判断当前拦截器的索引有没有超过 拦截器总数量-1
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();//调用目标方法
}
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);//默认值为-1所以先++
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// 执行带参数的动态方法匹配
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 {.
// 动态参数匹配失败,则跳过该拦截器
return proceed();
}
}
else { // MethodInterceptor类型的,不需要进行带参数的动态校验了
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); // 传入责任链处理器对象是关键
}
}
责任链处理器对象中,有着拦截器链的集合interceptorsAndDynamicMethodMatchers,以及一个拦截器链执行到哪的计数索引currentInterceptorIndex,其中需要注意的同样也是实现拦责任链模式的核心就是执行拦截器逻辑时,需要传入处理器对象,这样才能调回到处理器的逻辑中,继续进行下一个拦截器链的调用,从而一环一环的调用下去。我们可以发现,AOP的目标方法是在所有的拦截器都走过了的最后才触发调用的,我们总体看一下AOP的一个调用流程吧(如下图):
AOP责任链执行流程.png
写在最后
其实,聊到这里整个AOP的执行流程基本上就讲完了,但其实关于AOP的方法论才刚刚开始,从AOP看来Spring为我们提供了一种拦截代理对象然后进行过滤,构建责任链插入自定义逻辑的规范框架,我们可以在这个规范之上,进行二次开发,能够扩展出许多我们自己业务场景需要的,下节,我们继续聊聊Spring提供的这个模式是这么样的,以及Spring中还有哪些功能点套用了这一套模式。从功能点总结出思想,这样我们掌握了思想就是掌握了一类功能点的原理,对于我们二次开发的帮助是十分大的,我相信下一节的AOP模式总结对你帮助很大,敬请期待吧......
网友评论