Principle of Mybatis plug-in mechanism

In Mybatis, there is a powerful mechanism that allows us to quickly invade the underlying operations of Mybatis to expand the functions of Mybatis. This is the plug-in mechanism, and we can also call it the interceptor mechanism of Mybatis.

1. Basic principles

In the Mybatis architecture system, there are four major components (objects), namely Executor, StatementHandler, ParameterHandler, and ResultSetHandler . When we read the underlying source code of Mybatis, we can easily find that an interceptor.plugin method is called when creating these four major components in Mybatis. So our plug-in can only target these four objects. As the name suggests, interceptor means interceptor in Chinese. When it comes to interceptor, we can easily think of its function is to enhance the logic of a method before or after it is executed . There is actually a bit of AOP here ( Aspect programming) taste. As for the plug-in mechanism of Mybatis, its implementation is the aspect programming implemented by the dynamic proxy mechanism of jdk, which generates a proxy object for our four major objects, thereby enhancing the logic of our four major objects to execute corresponding methods. .

2. Basic usage of plug-in

Here is a brief talk about how our plug-in is used.

operation result:

In this way, the Intercepor is equipped, and the logic of the intercept method inside will be executed when the Executor executes the query method.

3. Implementation of the underlying principle of the plug-in

Why do we configure like the above figure to enable our plug-in to generate proxy objects for the four major components so that it can execute the enhanced logic of the intercept method inside? And it can be found that the printed log "intercepted target class" prints out the four major objects. Let's take a look at the source code to understand.

First of all, let's take a look at what operations the plug-in does after one of the four major components is created. Let's take Executor as an example to enter the method of initializing Executor.

Executor initialization logic is created in the process of creating SqlSession, so we come to a certain piece of source code when creating SqlSession

For the analysis of the creation process of SqlSession in Mybatis, you can go back and see:

Mybatis principle analysis (two) the creation process of SqlSession

There is an interceptorChain.pluginAll(executor) method executed here. Take a closer look at this method. When we create an Executor object, we pass this object into this method, and then return the Executor object to us. Guess it should be This object is packaged, we can click into this method to see

Inside is to get each Interceptor and then call the plugin method of the interceptor. This method takes our Executor object and returns it to us after execution. So this method is a method of packaging our four major objects. , And as we said before, the core of the plug-in mechanism is to generate a dynamic proxy for the target object, so when we implement our interceptor, we need to get the target class in the plugin method and generate a dynamic proxy for the target class The object then returned.

At this point, our goal is very clear, that is, to generate a dynamic proxy for the target class and return it in the plugin method of the custom Interceptor, then we now return to the plugin method of our custom Interceptor

It can be seen that the dynamic proxy object of the target class should be created inside, and a Plugin.wrap(target, this) method is directly called here, and the target class and its own object are passed in. Then we enter this method to take a look, and directly It’s not easy to understand the source code, so let’s debug it directly.

Here we find a getSignatureMap. First of all, we feel a little familiar from the name, right? The word signature seems to have been written somewhere, yes, we actually wrote this annotation on our custom Interceptor class, and this method will get all the @Signature annotations we wrote in Interceptor Information? Let's take a closer look at this method

 It can be found that this method is consistent with our guess, which is to analyze the information of the annotations we wrote. Then we will continue to look

We found that we got the class object of the target class (the target class here is the four major objects), and then passed the class object and the map returned above into a getAllInterfaces method, and the name should be the interface class object. Let’s go deeper.

 Finally, what is returned to us is an array of class objects containing four major objects.

And if the length of the array is not greater than 0, then it means that the target object does not need to be intercepted, and the target object is returned directly, and if it is greater than 0, it means that the target object needs to be intercepted, and then use jdk's dynamic proxy mechanism Generate the corresponding proxy object. So the entire wrap method is executed successfully. So here is a summary of the wrap method: First, this method will parse all the annotation information on our custom Interceptor class, and use the type attribute value as the key to generate a set collection of Method objects of the corresponding type according to the method and args attribute values. Is the value, and then according to this map to match whether there is an entry with the four component class objects as the key, if there is, put the interface class object implemented by the target object (that is, one of the four components) into the array, and finally Judge whether the size of this array is greater than 0, if it is greater than 0, it means that a dynamic proxy object needs to be created for the target object at this moment, otherwise, the target object can be returned directly.

After getting the proxy object of the target object, all methods of the target object that are executed afterwards will first execute the invoke method of the proxy object. And the class we passed in above that implements the InvocationHandler interface is the Plugin class, let’s enter the invoke method of this class to see

You may have a question here, that is, if the currently executed method is the method we specify to intercept, then return interceptor.intercept (new Invocation (target, method, args)) is executed, and you can see that this is a return With the return value of the intercept method of the custom interceptor, then we know that a method needs to be enhanced in the dynamic proxy, and then the original proxy must be executed after the enhancement, that is, method.invoke(target,args) must be returned The return value will execute the original code, so the return value of the intercept method above will not be able to execute the logic of the original method after the enhancement? In fact, it is not the case. The final return value of our intercept method is actually the return value of the code method.invoke(target,args), because we finally call the invocation.proceed() code in the intercept method, and in this code , We can take a look

In fact, this code is still method.invoke(target,args), so we will execute the logic of the original method after enhancing the method. 

Finally, summarize the principle of the plug-in mechanism of Mybatis: the plug-in is actually only effective for the four major objects, because it is hard to write in Mybatis. In the process of creating the four major objects, the objects will be thrown into the plug-in to generate the corresponding Dynamic proxy objects. The method of generating dynamic proxy objects is also implemented in the plugin method of the plug-in. Mybatis encapsulates the wrap method of a Plugins class for generating dynamic proxy objects. The principle of this method is to first get the Interceptor that we are customizing. The information annotated by @Intercepts and @Signature on the class is encapsulated into a map object with the type attribute value as the key, and the set collection of Method objects of the corresponding type is generated according to the method and args attribute values ​​as the value, and then in this map Ni caches which method of which class is to be intercepted and enhanced, and when the method of the proxy object is executed, it will enter the invoke method. In the invoke method, determine whether the currently executed method is in the map. If it exists, execute the enhanced method (intercept method) if it exists, and execute the logic of the original method if it does not exist. So the core of the plug-in is the dynamic proxy mechanism!

Guess you like

Origin blog.csdn.net/weixin_37689658/article/details/99686884