Beauty of mybatis source code: 2.7. Analyze the plugins element to complete the configuration of mybatis plug-in

Keywords: Programming Mybatis xml Blockchain Attribute

Parse the plugins element to complete the configuration of mybatis plug-in

> Click to see the usage of the typeAliases element

The plug-in mechanism of Mybtis is a very powerful function, which allows us to cut into the inside of Mybatis to perform some of the things we want to do during the operation of Mybatis.

Page helper, a popular paging plug-in of mybatis, is actually based on the plug-in function of mybatis.

pluginsDTD of Mybatis is defined as follows:

<!--ELEMENT plugins (plugin+)-->

<!--ELEMENT plugin (property*)-->a
<!--ATTLIST plugin
interceptor CDATA #REQUIRED
-->

One or more plugin child nodes must be defined under the plugins tag.

The plugin child node has a required property Interceptor, which points to a class that implements the Interceptor. At the same time, zero or more property tags are allowed under the plugin tag to configure the properties that the plug-in depends on when it runs.

For example:

<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100" />
  </plugin>
</plugins>

Interceptor is the plug-in interface definition provided by mybatis:

/**
 * Mybatis Definition of interceptor (plug-in) interface
 *
 * @author Clinton Begin
 */
public interface Interceptor {
    /**
     * Provide the proxy implementation of the intercepted method to complete additional business processing
     *
     * @param invocation agent
     */
    Object intercept(Invocation invocation) throws Throwable;

    /**
     * This method is used to process the intercepted object, return the object itself or generate a proxy object for it.
     * <p>
     * If the returned object is a proxy object, we can implement the proxy implementation for the specified method: {@ link ා intercept (invocation)}
     *
     * @param target Blocked objects
     */
    Object plugin(Object target);

    /**
     * Configure the properties that the plug-in depends on when it runs
     */
    void setProperties(Properties properties);

}

It defines three methods:

  • The intercept method is used to provide the proxy implementation of the intercepted method to complete additional business processing.
  • The plugin method is responsible for handling the intercepted object, returning the object itself or generating a proxy object for it.
  • The setProperties method is used to configure the properties that the plug-in depends on when it runs.

Among them, the input parameter of the intercept method is an object of type Invocation, and the Invocation object is a method reflection operation encapsulation object provided by mybatis. It maintains the specific method object and its runtime dependent parameters.

Invocation defines three properties:

/**
 * Represented object
 */
private final Object target;
/**
 * Represented method
 */
private final Method method;
/**
 * The input parameter of the proxied method
 */
private final Object[] args;

The assignment of these three properties is completed in the construction method of Invocation:

public Invocation(Object target, Method method, Object[] args) {
     this.target = target;
     this.method = method;
     this.args = args;
 }

At the same time, Invocation not only provides getter methods for these three properties, but also provides a processed method for completing method reflection operations

/**
 * Complete method call by reflection
 *
 * @return Method return value
 */
public Object proceed() throws InvocationTargetException, IllegalAccessException {
    return method.invoke(target, args);
}

After adding the basic knowledge, we return to the analysis operation of plugins element:

// Plug in configuration
pluginElement(root.evalNode("plugins"));

In the pluginElement method, XmlConfigBuilder will process all the plugin sub nodes in turn, obtain the property value of its interceptor, and give it to the resolveClass(String) method of BaseBuilder to obtain the specific plug-in implementation class,

And read all the property property configuration under the plugin sub node to generate the Properties object.

/**
 * Parsing plugins nodes
 *
 * @param parent plugins node
 */
private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
        for (XNode child : parent.getChildren()) {
            // Handle every interceptor
            String interceptor = child.getStringAttribute("interceptor");
            // Get runtime property configuration
            Properties properties = child.getChildrenAsProperties();
            // Getting plug-in implementation
            Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
            // Initialize plug-in configuration
            interceptorInstance.setProperties(properties);
            // Register the plug-in and register a new plug-in instance in the plug-in responsibility chain
            configuration.addInterceptor(interceptorInstance);
        }
    }
}

>Because the resolveClass method is used to obtain the implementation class of the plug-in, the configuration of the plug-in also supports the alias mechanism.

After that, we get the specific plug-in object instance through reflection, and call its setProperties method to configure the properties it needs to use at runtime.

After the appeal operation is completed, the addInterceptor(Interceptor) method of configuration is called to save the plug-in.

/**
 * Add an interceptor
 * @param interceptor Interceptor
 */
public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
}

In the addInterceptor(Interceptor) method of configuration, the work of adding plug-ins is left to the addInterceptor method of interceptor chain to complete.

The interceptorchain property is hard coded as an instance of interceptorchain type in Configuration:

/**
 * Plug in blockchain (Mybatis plug-in)
 */
protected final InterceptorChain interceptorChain = new InterceptorChain();

InterceptorChain defines an attribute of type list < interceptor > interceptors for storing all plug-in implementation classes in mybatis.

/**
 * All plug-ins
 */
private final List<interceptor> interceptors = new ArrayList&lt;&gt;();

The addInterceptor method exposed externally is used to add an Interceptor implementation to the interceptors collection:

/**
 * Add a new {@ link Interceptor {} instance
 */
public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
}

In addition to the addInterceptor method, InterceptorChain also exposes getInterceptors and pluginAll methods.

Where getInterceptors is used to get all current Interceptor instances:

/**
 * Get all interceptors
 */
public List<interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
}

The pluginAll method is responsible for calling the plugin methods of all Interceptor instances in turn to process the target object.

/**
 * Call {@ link interceptor {plugin (object)} method of all plug-ins to wrap the specified object
 */
public Object pluginAll(Object target) {
    // The multi-layer interceptor will form a responsibility chain by acting on the target object
    for (Interceptor interceptor : interceptors) {
        // Process the intercepted object, return the object itself or generate a proxy object for it.
        target = interceptor.plugin(target);
    }
    return target;
}

If multiple interceptors proxy the target object, a responsibility chain will be formed among the multi-level agents.

Responsibility chain mode (responsibility chain mode) is also a common behavioral design mode:

  • The chain of responsibility pattern is a chain structure composed of multiple objects, in which the former holds the reference of the latter through the link
  • When a user initiates a request to the responsibility chain, he / she does not know which object in the responsibility chain the request will be processed by. In this way, the user only needs to directly request to the responsibility chain, and does not need to initiate a request for each specific object

The responsibility chain mode can be divided into pure responsibility chain and impure responsibility chain according to different ways of processing requests:

  • Pure responsibility chain:

>In the process of processing the request, each node can only process or not process the request. At the same time, the request must be processed by a node in the responsibility chain. When the node processes the request, it will not continue to postpone the request, that is, terminate the request and continue to run in the link

  • Impure responsibility chain

>In the impure responsibility chain mode, a request may be processed by multiple nodes, that is, each node may process a part of the request, and its subsequent nodes may also process the part of the request, or even, there may be no node processing the request

In theory, the responsibility chain of agent formed by interceptors should be impure.

Pay attention to me and learn more together

Posted by mistercoffee on Fri, 26 Jun 2020 20:44:20 -0700