MyBatis plug in development

1. Principle of mybatis plug-in

MyBatis has plug-ins involved in the creation of four objects. The plug-in can use the dynamic proxy mechanism to wrap the target object layer by layer, and achieve the effect of intercepting before the target object executes the target method.
When creating four objects:

  1. Every object created is not directly returned
  2. Instead, we will first call the interceptorChain.pluginAll() method, pluginAll() to get all interceptors (interfaces that the plug-in needs to implement); call interceptor.plugin(target); return the object wrapped by target
//pluginAll source code
public Object pluginAll(Object target) {
   for (Interceptor interceptor : interceptors) {
     target = interceptor.plugin(target);
   }
   return target;
 }
  1. Plug in mechanism: when four objects are created, they will be intercepted by plug-ins. Our plug-ins can create proxy objects for the four objects. Before and after executing the methods of the four objects, the methods of proxy objects can be executed first (similar to AOP of tangent programming in Spring). Proxy objects can be intercepted to each execution of the four objects.

2. Plug in writing

Steps:

  1. Writing the implementation class of Interceptor
  2. Use @ Intercepts annotation to complete plug-in signature and tell MyBatis which method the current plug-in uses to intercept which object
  3. Register the written plug-in to the global configuration file

Plug in example:

import java.util.Properties;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

/**
 * 2.Complete plug-in signature:
 *		Tell MyBatis which method the current plug-in uses to intercept which object
 *	type: Indicates which object to block
 *	method: Which method represents the intercepted object
 *	args: List of parameter types of blocked methods (multiple parameters are represented by arrays)
 */
// Intercepts the parameterize method of StatementHandler. The parameter type of parameterize method is Statement
@Intercepts(
		{
			@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
		})
//1. Implement the Interceptor interface
public class MyFirstPlugin implements Interceptor{

	/**
	 * intercept: Intercept:
	 * 		The execution of the target method of intercepting the target object;
	 * When the intercepted object wants to execute the intercepted method, the method will execute. No other time.
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod());
		//Dynamically change the parameters of sql operation: before employee 1, actually query employee 3 from the database
		Object target = invocation.getTarget();
		System.out.println("Currently blocked objects:"+target);
		//Get: statementhandler = = > parameterhandler = = > parameterobject
		//Get target's metadata
		MetaObject metaObject = SystemMetaObject.forObject(target);
		Object value = metaObject.getValue("parameterHandler.parameterObject");
		System.out.println("sql The parameters used in the statement are:"+value);
		//Parameters to be used after modifying sql statement
		metaObject.setValue("parameterHandler.parameterObject", 11);
		//Execute the target method. If the target method is not called here, the target method will never be executed
		Object proceed = invocation.proceed();
		//Return the return value after execution
		return proceed;
	}

	/**
	 * plugin: 
	 * 		Wrapping the target object: the so-called wrapping is to create a proxy object for the target object
	 * This method is called when any one of the four objects is created.
	 * But the real object to wrap is the object specified in the @ concepts annotation.
	 */
	@Override
	public Object plugin(Object target) {
		// TODO Auto-generated method stub
		//We can use Plugin's wrap method to wrap our target object with the current Interceptor
		System.out.println("MyFirstPlugin...plugin:mybatis Objects to be wrapped"+target);
		//target indicates the object to be wrapped, and the second parameter indicates which interceptor is used for wrapping (this indicates the current interceptor is used)
		Object wrap = Plugin.wrap(target, this);
		//Returns the dynamic agent created for the current target
		return wrap;
	}

	/**
	 * setProperties: 
	 * 		Set the property property when the plug-in is registered (configured in MyBatis global configuration file)
	 */
	@Override
	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub
		System.out.println("Plug in configuration information:"+properties);
	}

}

3. Register plugin in global configuration file

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!--plugins: Registration plug-in  -->
	<plugins>
		<plugin interceptor="com.atguigu.mybatis.dao.MyFirstPlugin">
		<!-- Specified here property Will be packaged as Properties Object, easy to use during program running-->
			<property name="username" value="root"/>
			<property name="password" value="123456"/>
		</plugin>
		<!-- Configuration of multiple plug-ins-->
	<!--	<plugin interceptor="com.atguigu.mybatis.dao.MySecondPlugin"></plugin>
	</plugins>
	 -->
</configuration>

3. Multiple plug-ins intercept the same target object

Define a second plug-in to intercept the same target object:

import java.util.Properties;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
// The second plug-in is defined here, which intercepts the same parameterize method of the same StatementHandler object
@Intercepts(
		{
			@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
		})
public class MySecondPlugin implements Interceptor{

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		System.out.println("MySecondPlugin...intercept:"+invocation.getMethod());
		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		// TODO Auto-generated method stub
		System.out.println("MySecondPlugin...plugin:"+target);
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub
	}

}

Register plugin in the global configuration file:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!--plugins: Registration plug-in  -->
	<plugins>
		<plugin interceptor="com.atguigu.mybatis.dao.MyFirstPlugin">
		<!-- Specified here property Will be packaged as Properties Object, easy to use during program running-->
			<property name="username" value="root"/>
			<property name="password" value="123456"/>
		</plugin>
		<!-- Configuration of multiple plug-ins, register the second plug-in-->
		<plugin interceptor="com.atguigu.mybatis.dao.MySecondPlugin"></plugin>
	</plugins>
</configuration>

Multiple plug-ins, multi-layer agents:

  1. Multiple plug-ins will generate multi-level proxy objects. The order of creation is determined by the order of plug-ins declaration in the global configuration file. The first declaration will be wrapped first.
  2. When calling, the plug-in's intercept method will be called layer by layer from the outside to the inside

    As shown in the figure above, two plug-ins are defined to wrap StatementHandler objects. First, MyFirstPlugin is used to wrap the StatementHandler objects, and then MySecondPlugin is used to wrap them. When StatementHandler methods need to be executed, from the outside to the inside, MyFirstPlugin's intercept method is executed first, then MySecondPlugin's intercept method is executed, and finally the target is executed Method of StatementHandler for object. (remember this figure, it will be easy to understand when you encounter multi-level agents in the future.)
37 original articles published, 10 praised, 10000 visitors+
Private letter follow

Tags: Mybatis Apache SQL Java

Posted on Thu, 16 Jan 2020 10:20:49 -0500 by thirdeye