AOP process source code analysis - request call the whole process

This time I will talk about all the remaining AOP content. In fact, there is only one request to call the whole process. In the follow-up Spring series articles, only boot and cloud are left. I should be able to talk about boot before my interview, but cloud is because I have been working in the insurance industry for these years, and it is not very clear now. What Distributed can remember now is the dubbo system. This can only be discussed later. If it is necessary to go to the interview, I will look at the cloud and sort it out. For the corresponding article, if it doesn’t work, you can only wait until the interview is over, but it should be finished this year.

Source code analysis

After talking some nonsense, now we will start this source code analysis. The old driver starts the car and sits firmly.

The last aop article talked about the creation of agents (there are only two aops in total, so I don’t need to mark the links again). It is divided into two ways: cglib and jdk. We usually use jdk to create, cglib is not familiar with case, it is not recommended to use. What we are going to talk about this time is also the whole process of request calling after jdk generates a proxy.

The first thing you need to see is the JdkDynamicAopProxy object. The proxy object needs to inherit the InvocationHandler object, so the invoke method must be rewritten (this will also be mentioned later in the dubbo service call process, please pay attention here) when the request When calling the method of the proxy object, we will go to this invoke method, so we follow up this method directly.

Acquisition and construction of interceptor chain, proxy object instance, executor -invoke method

Follow up method.

First of all, it will ignore whether the method is an Object method such as equals, hashCode, etc., because these basic methods will not be proxied.

Then it is to get the instance of the proxy object

// 2.拿到我们被代理的对象实例
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);

Then it is to get the interceptor chain we parsed before

// 【*】3.获取拦截器链:例如使用@Around注解时会找到AspectJAroundAdvice,还有ExposeInvocationInterceptor
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

At this point, I will no longer pay attention to other content, mainly to intercept calls.

Here first build a ReflectiveMethodInvocation object, which I understand to be an executor object, and then directly call the proceed method of the object.

// 6.如果存在拦截器,则创建一个ReflectiveMethodInvocation:代理对象、被代理对象、方法、参数、
// 被代理对象的Class、拦截器链作为参数创建ReflectiveMethodInvocation
MethodInvocation invocation =
  new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
// 7.触发ReflectiveMethodInvocation的执行方法(逐一调用chain中的每一个拦截方法的proceed)
retVal = invocation.proceed();

Call the interceptor and execute the method of the called object-proceed method

Follow up the proceed method.

Then you will see a very interesting logic. Let me explain here. The value of the currentInterceptorIndex attribute here is -1, and the set corresponding to it is the interceptor chain, which is the size of the set of advisors - 1, and then invokeJoinpoint method It is to execute the method that the proxy object is really called.

The following is to obtain the specific interceptor according to the subscript index in the interceptor chain. Remember that the value of the currentInterceptorIndex property will be +1 after this code is executed. Here are two points to remember: 1. When the property value is equal to the size of the interceptor chain, the method being called by the proxy object will be executed; 2. As long as the specific interceptor is obtained, the value of the currentInterceptorIndex property will be +1 .

// 1.如果所有拦截器都执行完毕(index是从-1开始,所以跟size - 1比较),则直接使用反射调用连接点(也就是我们原本的方法)
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    
    
  return invokeJoinpoint();
}

// 2.每次调用时,将索引的值递增,并通过索引拿到要执行的拦截器
Object interceptorOrInterceptionAdvice =
  this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

Then we went on to look down and directly looked at the transfer of the interceptor.

return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);

At present, our interceptor is still the configuration in the previous article, that is, three interceptors, surround, front, and rear, but spring will add an initial interceptor to us by default, which we don't need to look at for now.

image-20220428215745492

By the way, the order of interceptors is surround, pre-position, and post-position. As for interceptors at the same level, the order of configuration analysis should be seen before. The first configuration is executed first, but the post-position is the opposite. As for why, let’s continue to look at the code.

Interceptor object: AspectJAroundAdvice object

We now follow directly into the invoke method of the surround interceptor object: the AspectJAroundAdvice object.

Here, two objects will be encapsulated first, and then the invokeAdviceMethod method will be called directly. This method is very winding, but the purpose is to call the surrounding interception method we wrote before.

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    
    
  // 1.这边的mi就是我们的ReflectiveMethodInvocation,
  // ReflectiveMethodInvocation实现了ProxyMethodInvocation接口,所以这边肯定通过校验
  if (!(mi instanceof ProxyMethodInvocation)) {
    
    
    throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
  }
  // 2.将mi直接强转成ProxyMethodInvocation,mi持有代理类实例proxy、被代理类实例target、被代理的方法method等
  ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
  // 3.将pmi封装成MethodInvocationProceedingJoinPoint(直接持有入参mi,也就是ReflectiveMethodInvocation的引用)
  ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
  // 4.拿到pointcut的表达式
  JoinPointMatch jpm = getJoinPointMatch(pmi);
  // 5.调用增强方法
  return invokeAdviceMethod(pjp, jpm, null, null);
}

Let's go back to the surround intercept method, where there will be a ProceedingJoinPoint object, which is the encapsulation of our ReflectiveMethodInvocation object above, and the proceed method called here is the same as our above call.

This means that if the current surround interception has not been executed, it will return to the proceed method and go to the next interceptor. Note that without this call, there will be no calls to all subsequent interceptors.

@Around("pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws InterruptedException {
    
    
  System.out.println("around advice start");
  try {
    
    
    Object result = proceedingJoinPoint.proceed();
    System.out.println("around advice end");
    return result;
  } catch (Throwable throwable) {
    
    
    throwable.printStackTrace();
    return null;
  }
}

Pre-interceptor: MethodBeforeAdviceInterceptor object

Then we look at the pre-blocker.

Pre-interceptor: MethodBeforeAdviceInterceptor object, the content is relatively simple, it is to execute the pre-method we wrote before, and then continue to call back the proceed method, which is equivalent to returning to the call of the next interceptor.

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    
    
  this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
  return mi.proceed();
}

Post interceptor: AspectJAfterAdvice object

Continue to look at the post-interceptor: AspectJAfterAdvice object, which is also simple here, but first of all, return to the proceed method, and then execute your own method call after the next interceptor call is successful.

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    
    
  try {
    
    
    return mi.proceed();
  }
  finally {
    
    
    invokeAdviceMethod(getJoinPointMatch(), null, null);
  }
}

Process summary

Now to sort out the process, the first is the interceptor call provided by spring. When the interceptor is executed, the proceed method is called, and the next surround interceptor is followed by the surround interception, and the proceed method is called to the front interceptor, and then by The pre-interceptor calls the proceed method to the post-interceptor. Note that my interceptor here has been completed, so the previous currentInterceptorIndex attribute value should be 3 now, and the size of the interceptor chain -1 is also 3, then the above-mentioned invokeJoinpoint method will be used, so that the delegated The method being called by the object. At the same time, note that after the call is completed, the interceptor cannot be obtained below, which means that the proceed method called by the post-interceptor is finished. Currently, it continues to go in the post-interceptor, so that It will take the interception method of the rear interceptor.

Having said so much, it’s a bit convoluted, so a brief description: proceed method call interceptor 1 (identify the first call, subsequent accumulation) -> spring default interceptor -> proceed method call interceptor 2 -> surround interceptor method- > proceed method call interceptor 3 -> pre-interceptor method -> proceed method call interceptor 4 -> post-interceptor method -> proceed method call interceptor 5 -> note that there is no interceptor here, so go invokeJoinpoint method.

The process is not finished, and there is still going back: proceed method call interceptor 5 -> post interceptor method -> proceed method call interceptor 4 -> front interceptor method -> proceed method call interceptor 3 -> surround interceptor Method -> proceed method call interceptor 2 -> spring default interceptor -> proceed method call interceptor 1 -> finally is the end of interceptor 1.

In this way, we can know why, the post-interceptor, the configuration is executed after the previous one, because the post-interceptor does not execute its own interception method first, but calls the next interceptor, so the configuration is at the end Interceptors are executed before the previous post-interceptors.

Summarize

The call process is actually not very complicated, but it is quite convoluted. After understanding it, you will find that this is quite interesting. It does not use a loop, but it also achieves the effect of a loop, and it is more clever. This is the setting mode of the task chain.

This time it ends here, and the content is much less than before, because all the foreshadowing has been done before, and many of them are references here. As for the follow-up spring articles, let’s see the situation. In the next stage, we should first look at jdk Source code, there are a lot of interview questions this time. If the interview is done later, I will make a series of interview questions, first. The terminal is here, get off.

Appendix Spring Source Code Analysis Series Articles

IOC

time article
2022-03-09 A brief introduction to the basic concepts of Spring and the IOC process
2022-03-11 IOC process analysis - creation of BeanFactory
2022-03-14 IOC process analysis - BeanFactoyPostProcessor and BeanPostProcessor
2022-03-15 IOC process analysis - instantiation and initialization
2022-03-17 IOC process analysis - circular dependency

AOP

time article
2022-03-19 A brief introduction to the basic concepts of Spring and the IOC process
2022-03-20 AOP process source code analysis - request call the whole process

Guess you like

Origin blog.csdn.net/qq_39339965/article/details/124489143
Recommended