美文网首页
AOP源码分析之目标方法执行过程

AOP源码分析之目标方法执行过程

作者: 会上树的程序猿 | 来源:发表于2020-05-29 16:50 被阅读0次

我们先来简单的回忆下上节Aop代理对象的创建过程,整个创建的入口都在方法postProcessAfterInitialization中:

    1. wrapIfNecessary(bean, beanName, cacheKey)中来判断是否需要包装
    • 1.1. 获取当前bean的增强器(通知方法)--->specificInterceptors
    • 1.2. 找到候选的所有增强器
    • 1.3. 获取到能在bean使用的增强器
    • 1.4. 给增强器排序
    1. 保存当前bean在advisedBeans中
    1. 如果当前bean需要增强,创建当前bean的代理对象
      • 3.1. 获取所有的通知方法
      • 3.2. 保存到proxyFactory
      • 3.3. 创建代理对象【CGLIB】
  • 4.给容器中返回当前组件使用cglib增强了的代理对象

上述就是代理对象创建的过程,接着我们来看看目标方法的执行过程,即下面的代码:

 //Aop测试
@Test
public void testAop(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAop.class);
    MathCalculator calculator = applicationContext.getBean(MathCalculator.class);
    calculator.div(10,5);

    applicationContext.close();
}

也就是我们的div方法,我们给方法打个断点,继续跟踪代码:

Aop源码分析.png

从截图来看我们获取到的是一个代理对象

image.png

红色框中的是我们的5个增强器(通知方法),首先我们来到类CglibAopProxy#intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)方法,其中方法的参数如下:

Aop源码分析1.png

知道了方法的方法参数,大致猜测下该方法的主要的作用是拦截我们目标方法的执行,至于拦截之后都做了些什么,我们具体来看看代码实现:

  • 1.首先发现通过传入参数为目标方法和代理对象来获取拦截器链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

chain 翻译过来的意思就是链的意思,我们进去看法看看

      1. 首先以我们目标方法的完整路径为参数尝试着从缓存中获取增强器(通知方法)
AdvisedSupport.MethodCacheKey cacheKey = new AdvisedSupport.MethodCacheKey(method);
List<Object> cached = (List)this.methodCache.get(cacheKey);
  • 1.2. 如果没有拿到,通过AdvisorChainFactory去获取,并保存在缓存中,以供后续直接使用
if (cached == null) {
        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);
        this.methodCache.put(cacheKey, cached);
    }

我们跟踪代码进去#getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) ,其中方法参数如下图所示:

image.png
  • 1.2.1. 首先我们通过增强器适配器的注册工厂获取已经注册在容器中的适配器实例
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
image.png

这里为啥要获取这些适配器了,后面有用,接着往下看:

  • 1.2.2. 创建一个用来保存所有拦截器链的list 【interceptorList】

  • 1.2.3. 进行遍历分类处理我们获取到的增强器,并转为Interceptor

  MethodInterceptor[] interceptors = registry.getInterceptors(advisor);

转的过程是通过对应适配器进行转化的,这也就是为啥一开始就获取具体的适配器的原因

  • 1.2.4.最后将转化之后的拦截器进行添加到interceptorList中并返回,具体的拦截器链如下图是所示:
image.png

其实也就是我们的4个通知方法【增强器】和一个默认的拦截器ExposeInvocationInterceptor

需要注意的一点是:
a:如果是MethodInterceptor,直接加入到集合中
b:如果不是,使用AdvisorAdapter将增强器转为MethodInterceptor
我们需要注意这两点,自己可以Dbug跟踪一下就明白了,由于代码篇幅太长就不在罗列代码片段了,还是来到我们类CglibAopProxy#intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)方法中,继续往下跟踪代码:

  • 2 .当我们拿到拦截器链的时候,然后进行判断,如果没有拦截器链,发现这里直接调用#invoke(...)来执行我们的目标方法
  if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = methodProxy.invoke(target, argsToUse);
            } 

当然我们的代码这里是不会进来的,下面这段代码就是有拦截器链的执行代码(注意的一点是这里的拦截器链利用MethodInterceptor机制将我们的每一个通知方法又被包装为方法拦截器)

retVal = (new CglibAopProxy.CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy)).proceed();

上述这段代码片段我们需要注意的是proceed(),进去方法来到:

public Object proceed() throws Throwable {
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return this.invokeJoinpoint();
    } else {
        Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
            Class<?> targetClass = this.targetClass != null ? this.targetClass : this.method.getDeclaringClass();
            return dm.methodMatcher.matches(this.method, targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
        } else {
            return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
        }
    }
}

上述方法是执行我们目标方法的入口,所有的操作都在这里完成,我们来看一下具体的执行过程,首先来说明下:

  • 3 .方法参数说明:
    • 3.1. currentInterceptorIndex :表示当前拦截器链 的索引,默认值为-1
 private int currentInterceptorIndex = -1;
  • 3.2. interceptorsAndDynamicMethodMatchers表:就是我们当前的拦截器链
image.png

接着往下走:

  • 3.3. 如果当前索引和拦截器数组大小-1相等或者没有拦截器链时,直接执行目标方法
 if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return this.invokeJoinpoint();
    }

很显然,第一步我们是不进if语句的,接着往下跟踪代码:

  • 3.4.首现通过下面代码来获取我们的第一个方法拦截器
 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

首现让拦截器链的索引+1,然后在方法拦截器数组中去获取第一个,这里也就是ExposeInvocationInterceptor 这个方法拦截器,继续跟踪代码:

else {
        Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
            Class<?> targetClass = this.targetClass != null ? this.targetClass : this.method.getDeclaringClass();
            return dm.methodMatcher.matches(this.method, targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
        } else {
            return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
        }
  • 首现是比对当前获取到的拦截器是否是InterceptorAndDynamicMethodMatcher类型的

  • 如果不是直接调用invoke()执行目标方法

  • 如果是的话,首现是链式获取到每一个拦截器执行对应的invoke(),这里采用的是回溯调用的机制,也就是我们每一拦截器等待前一个拦截器执行完之后再来执行当前的,这也是拦截器的链式机制,保证每一个通知方法于目标方法的执行时有顺序的

这一块的代码逻辑大家感兴趣的可以自己Dbug走一下代码就明白了,总之到这里关于AOP的源码分析完结了,简单的给大家总结下大体的AOP思路

  • 首先通过注解EnableAspectJAutoProxy 开启我们的AOP功能

  • 通过注解EnableAspectJAutoProxy我们发现会给容器注册一个AnnotationAwareAspectJAutoProxyCreator的组件(后置处理器)

  • 在我们IOC容器的创建过程中:

    1. 利用#refresh()方法中的registerBeanPostProcessors(beanFactory)完成我们AnnotationAwareAspectJAutoProxyCreator的注册和创建
    2. 接着利用#refresh()方法中的this.finishBeanFactoryInitialization(beanFactory)来完成剩余单实例bean的创建,其主要的是完成我们自己的日志切面组件和业务组件的创建
      2.1. 在创建的过程中AnnotationAwareAspectJAutoProxyCreator利用自己作为【后置处理器的角色】在我们组件创建之前进行拦截,其主要的目的是先尝试着返回一个代理bean,如果存在直接使用该代理bean
    1. 组件创建完后之后,判断当前组件是否需要增强
      3.1.如果需要,将我们的通知方法进行 包装成增强器【advisor】,同时给业务组件创建一个CGLIB代理对象返回给容器中
    1. 目标方法的执行
      4.1. 首先我们获取到目标方法的拦截器链,然后将拦截器链中每一个通知方法包装成一个个【MethodInterceptor】方法拦截器
      4.2. 然后利用拦截器的链式机制一次执行目标方法
  • 如果是没有异常的情况下:
    1.执行的顺序为前置通知-->目标方法-->后置通知-->返回通知

  • 出现异常的情况下的执行顺序:
    2.前置通知-->目标方法-->后置通知-->异常通知

那么关于整个Aop的分析就完结了

相关文章

网友评论

      本文标题:AOP源码分析之目标方法执行过程

      本文链接:https://www.haomeiwen.com/subject/xohzahtx.html