• Spring——AOP原理及源码五【系列完】


    前情回顾:

      上一篇中,通过 wrapIfNecessary 方法,我们获取到了合适的增强器(日志方法)与业务

    类进行包装,最终返回了我们业务类的代理对象。

      

     

      本篇我们将从业务方法的执行开始,看看增强器(日志方法)是怎么在方法执行的前后和发

    生异常时被调用的。以及在文章的最后总结整个AOP的执行流程。

     

    1、调试的起点:

    给测试方法打上断点,然后一直跳到下一个断点直到执行方法,如下  

    接着进入方法,会被intercept方法拦截 

    进入断点:

     1 @Override
     2         public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
     3             Object oldProxy = null;
     4             boolean setProxyContext = false;
     5             Class<?> targetClass = null;
     6             Object target = null;
     7             try {
     8                 if (this.advised.exposeProxy) {
     9                     // Make invocation available if necessary.
    10                     oldProxy = AopContext.setCurrentProxy(proxy);
    11                     setProxyContext = true;
    12                 }
    13                 // May be null. Get as late as possible to minimize the time we
    14                 // "own" the target, in case it comes from a pool...
    15                 target = getTarget();
    16                 if (target != null) {
    17                     targetClass = target.getClass();
    18                 }
    19                 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
    20                 Object retVal;
    21                 // Check whether we only have one InvokerInterceptor: that is,
    22                 // no real advice, but just reflective invocation of the target.
    23                 if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
    24                     // We can skip creating a MethodInvocation: just invoke the target directly.
    25                     // Note that the final invoker must be an InvokerInterceptor, so we know
    26                     // it does nothing but a reflective operation on the target, and no hot
    27                     // swapping or fancy proxying.
    28                     Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
    29                     retVal = methodProxy.invoke(target, argsToUse);
    30                 }
    31                 else {
    32                     // We need to create a method invocation...
    33                     retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
    34                 }
    35                 retVal = processReturnType(proxy, target, method, retVal);
    36                 return retVal;
    37             }
    38             finally {
    39                 if (target != null) {
    40                     releaseTarget(target);
    41                 }
    42                 if (setProxyContext) {
    43                     // Restore old proxy.
    44                     AopContext.setCurrentProxy(oldProxy);
    45                 }
    46             }
    47         }
    intercept


    intercept 方法从上往下看:

    16~18:获取目标类(注意不是代理对象)

      

     

    19:通过目标类和目标方法获取拦截器链

    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);


     

    2、重点探究获取拦截器链的过程

    进入 getInterceptorsAndDynamicInterceptionAdvice

     

    继续进入 this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass)

     1 @Override
     2     public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
     3             Advised config, Method method, Class<?> targetClass) {
     4 
     5         // This is somewhat tricky... We have to process introductions first,
     6         // but we need to preserve order in the ultimate list.
     7         List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
     8         Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
     9         boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
    10         AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    11 
    12         for (Advisor advisor : config.getAdvisors()) {
    13             if (advisor instanceof PointcutAdvisor) {
    14                 // Add it conditionally.
    15                 PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
    16                 if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
    17                     MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
    18                     MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
    19                     if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
    20                         if (mm.isRuntime()) {
    21                             // Creating a new object instance in the getInterceptors() method
    22                             // isn't a problem as we normally cache created chains.
    23                             for (MethodInterceptor interceptor : interceptors) {
    24                                 interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
    25                             }
    26                         }
    27                         else {
    28                             interceptorList.addAll(Arrays.asList(interceptors));
    29                         }
    30                     }
    31                 }
    32             }
    33             else if (advisor instanceof IntroductionAdvisor) {
    34                 IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
    35                 if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
    36                     Interceptor[] interceptors = registry.getInterceptors(advisor);
    37                     interceptorList.addAll(Arrays.asList(interceptors));
    38                 }
    39             }
    40             else {
    41                 Interceptor[] interceptors = registry.getInterceptors(advisor);
    42                 interceptorList.addAll(Arrays.asList(interceptors));
    43             }
    44         }
    45 
    46         return interceptorList;
    47     }
    getInterceptorsAndDynamicInterceptionAdvice

     

    以上代码从上往下看:

    5:创建拦截器链

    List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length)

    12~32:

    • 遍历所有增强器
    • 经过一系列判断,将增强器放入interceptorList中 :
    • interceptorList.addAll(Arrays.asList(interceptors))

    46:将拦截器链返回

     

    接下来将拦截器链返回,并存入缓存中

     

    最后将拦截器链返回

     

     这就是拦截器链获取的过程


     

    接下来来到 intercept 方法的真正执行部分:

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

    通过new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy) 获取到方法拦截器链 

    一进来先调用父类方法:

     

     设置好代理对象、目标类、目标方法、拦截器链等一系列属性:

     

    接着一路返回后调用 proceed 方法进行执行:

     1 @Override
     2     public Object proceed() throws Throwable {
     3         //    We start with an index of -1 and increment early.
     4         if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
     5             return invokeJoinpoint();
     6         }
     7 
     8         Object interceptorOrInterceptionAdvice =
     9                 this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    10         if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
    11             // Evaluate dynamic method matcher here: static part will already have
    12             // been evaluated and found to match.
    13             InterceptorAndDynamicMethodMatcher dm =
    14                     (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
    15             if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
    16                 return dm.interceptor.invoke(this);
    17             }
    18             else {
    19                 // Dynamic matching failed.
    20                 // Skip this interceptor and invoke the next in the chain.
    21                 return proceed();
    22             }
    23         }
    24         else {
    25             // It's an interceptor, so we just invoke it: The pointcut will have
    26             // been evaluated statically before this object was constructed.
    27             return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    28         }
    29     }
    proceed

     

    3~6:可以看到有一个从-1开始的索引,这是用来记录当前执行次数的(这里的size为5对应我们的五个增强器)

     

     

    8~9:每次从拦截器链中获取一个增强器,索引加一

    10:判断这个增强器是不是 InterceptorAndDynamicMethodMatcher 类型,我们这里判断不满足,来到了else,返回调用 invoke 方法的结果

     


     

    接下来我们进入这个执行过程

    invoke方法调用proceed方法

     

     

    来到proceed方法继续判断索引大小

     

    往下走又来到 invoke 方法,从下图可以看到当前是异常增强器的invoke()

     

    进入invoke

    先 return 调用 proceed 方法

    可以看到下图中 catch 部分,说明如果有出现异常,会在catch部分调用增强器方法,并抛出异常

     

    接下来又是调用AfterReturning的invoke过程

     

    下图可以看到又调用了proceed 

     

    中间的过程也一样,这里就不演示了

    最终我们的索引来到末尾

    整个过程开始从内到外执行日志方法

    开始调用日志方法打印:

     

     

    抛出异常

     

     

    最终拦截器链调用完毕,得到结果:

     

    以上可以看到,整个执行流程是一个递归调用的过程,对之前排好序的拦截器链,通过索引判断界限,一层一层往里调用,最终递归回来一层层执行增强器(日志方法)

     


     

     

    3、AOP总结:

    通过@EnableAspectJAutoProxy 注解,给容器中注册 AnnotationAwareAspectJAutoProxyCreator,这个组件是一个后置处理器

    会在每一个bean创建之前执行它的后置处理器方法来获取对应增强器,并获取到目标代理对象

    在执行切面方法时,通过代理对象和增强器等信息,获取到拦截器链

    拦截器链在包装处理后进入执行流程,嵌套调用后执行增强器方法

    aop uml图

    以下UML图是我对AOP的理解,如果有不对之处,欢迎大家指出

     

  • 相关阅读:
    H5页面引用百度地图绘制车辆历史轨迹
    js 银行卡号校验
    PC页面客服微信QQ弹窗(鼠标移入显示移出隐藏)
    js 识别二维码
    js 生成二维码
    apicloud APP沉浸式状态栏设置
    微信公众号页面video标签播放视频兼容问题
    EasyUI 导出数据表格 (Export DataGrid)
    c#部署程式到服務器插入數據到oracleDB亂碼
    asp.net JS使用MVC下載(導出Excel)
  • 原文地址:https://www.cnblogs.com/Unicron/p/12441483.html
Copyright © 2020-2023  润新知