preface
- Mybatis paging plug-in I believe we have used, so can know the implementation principle? The paging plug-in is implemented using the plugin mechanism in Mybatis to perform paging before and after Executor’s Query execution.
- This article will introduce the following Mybatis plug-in mechanism and how it is implemented at the bottom.
Environment configuration
- Everything in this article is based on Mybatis3.5 and SpringBoot-2.3.3.release.
What is a plug-in?
-
Plug-ins are one of the most important features in Mybatis, enhancing specific methods for specific components.
-
MyBatis allows you to intercept calls at some point during the execution of a mapping statement. By default, MyBatis allows you to intercept method calls using plug-ins:
-
Executor: Update, Query, flushStatements, COMMIT, rollback, getTransaction, close, isClosed
-
ParameterHandler: getParameterObject, setParameters
-
ResultSetHandler: handleResultSets and handleOutputParameters
-
StatementHandler: prepare, parameterize, Batch, Update,query
How do I customize plug-ins?
-
Plug-in implementation is actually very simple, just need to achieve Mybatis provides the Interceptor interface, the source code is as follows:
Public interface Interceptor {// Intercepting method Object Intercept (Invocation) throws Throwable; // Return the interceptor’s proxy Object plugin(Object target); // Set some Properties void setProperties(Properties Properties);
}
For example
-
There is a requirement to tamper with the selectByUserId parameter value while executing Mybatis.
-
“Analysis” : Modifying SQL input parameters, which component and which method should tamper be blocked? As anyone who has studied the source code knows, the setParameters() method in ParameterHandler handles parameters. So surely intercepting this method is the most appropriate.
-
The custom plug-ins are as follows:
/ * *
- The @intercepts annotation marks this as an interceptor in which multiple @signatures can be specified
- @signature specifies which of the four object methods the interceptor intercepts
-
Type: The types of the interceptor's four major objectsCopy the code
-
Method: specifies the method name of the interceptorCopy the code
-
Args: The type of the input parameter, which can be multiple, specified according to the method's parameters to distinguish method overloadingCopy the code
*/ @Intercepts( { @Signature(type = ParameterHandler.class,method =”setParameters”,args = {PreparedStatement.class}) } ) public class ParameterInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) Throws system.out.println (” Invocation of interceptor: “+ Invocation. GetTarget ()); // Target Object = Invocation. GetTarget (); // Get the values of all the attributes in the target object, since ParameterHandler uses DefaultParameterHandler, So all the attributes are encapsulated in the MetaObject MetaObject. = SystemMetaObject forObject (target); String value = (String) metaObject.getValue(“mappedStatement.id”); metaObject.getValue(“mappedStatement.id”); / / if the specified query methods is the if (” cn. Cb. Demo. Dao. UserMapper. SelectByUserId “. The equals (value)) {/ / set the value of parameters is admin_1, namely is to set up id = admin_1, because there is only one parameter, SetValue (“parameterObject”, “admin_1”); metaObject.setValue(“parameterObject”, “admin_1”); Return invocation. Proceed (); }
@override public Object plugin(Object target) {return plugin.wrap (target, this); } @Override public void setProperties(Properties properties) { }Copy the code
}
-
An intercept method: a method that is eventually intercepted, the most important method.
-
Plugin method: return a proxy object, if there is no special requirements, directly use Mybatis utility class plugin can return.
-
SetProperties: Sets some properties, not important.
What annotations are used?
-
Custom plug-ins require two annotations, @intercepts and @signature.
-
Intercepts: Annotated on an implementation class, this class is the implementation class of a plug-in.
-
@signature: As an attribute of @intercepts, it indicates that certain methods in certain components of Mybatis need to be enhanced (more than one can be specified). Common attributes are as follows:
-
Class<? > Type () : specifies which component (Executor, ParameterHandler, ResultSetHandler, StatementHandler)
-
String method() : Specifies which method in the enhanced component, writing the method name directly.
-
Class<? >[] args() : parameter in method, must be one to one, can write multiple; This property is very reusable and distinguishes overloaded methods.
How to inject Mybatis?
- With the plug-in defined above, how do I inject it into Mybatis to make it work?
- “Premise” : Since the environment of this article is SpringBoot+Mybatis, we will explain how to inject plug-ins into Mybatis in SpringBoot.
- MybatisAutoConfiguration = SqlSessionFactory = SqlSessionFactory = SqlSessionFactory
- This. Interceptors is an Interceptor[] from a container.
-
As you can see from the above, the plug-in finally gets the Interceptor[] Bean from the IOC container, so we just need to inject this Bean into the configuration class, as follows:
/ * *
- @Configuration: This annotation indicates that the class is a Configuration class
*/ @Configuration public class MybatisConfig{
/ * * * @ Bean: Public Interceptor[] Interceptor[] interceptors(){public Interceptor[] interceptors(){ ParameterInterceptor = new ParameterInterceptor(); ParameterInterceptor = new ParameterInterceptor(); Return new Interceptor[]{parameterInterceptor}; }Copy the code
}
test
-
At this point, the custom plug-in has been injected into Mybatis, now test to see if it can be successfully executed? The test code is as follows:
@ Test void contextLoads () {/ / incoming is 1222 the UserInfo the UserInfo = userMapper. SelectByUserId (" 1222 "); System.out.println(userInfo); }Copy the code
-
The test code is passed in 1222, and since the plug-in has changed the input parameter, the query should be admin_1.
Principle analysis of plug-in
-
The principle of a plug-in is simple: it generates a Plugin when a component is created and intercepts it when a component method is executed. How does Mybatis work?
-
The four major components of Mybatis are created in the Configuration class of Mybatis. The specific method is as follows:
// Create Executor public Executor newExecutor(Transaction Transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } / / call pluginAll method, generates a proxy object executor = (executor) interceptorChain. PluginAll (executor); return executor; }
ParameterHandler public ParameterHandler newParameterHandler(MappedStatement MappedStatement, Object parameterObject, BoundSql boundSql) { ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); / / call pluginAll method, generates a proxy object parameterHandler = (parameterHandler) interceptorChain. PluginAll (parameterHandler); return parameterHandler; }
ResultSetHandler public ResultSetHandler newResultSetHandler(Executor Executor, MappedStatement MappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); / / call pluginAll method, generates a proxy object resultSetHandler = (resultSetHandler) interceptorChain. PluginAll (resultSetHandler); return resultSetHandler; }
Public StatementHandler newStatementHandler(Executor Executor, MappedStatement MappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); / / call pluginAll method, generates a proxy object statementHandler = (statementHandler) interceptorChain. PluginAll (statementHandler); return statementHandler; }
-
As you can see from the source code above, pluginAll() is executed to generate a proxy object in all four components. Specific how to generate, detailed explanation below.
How do I generate proxy objects?
-
The pluginAll() method is executed during the creation of all four components. The source code for this method is as follows:
Public Object pluginAll(Object target) {for (Interceptor Interceptor: Target = interceptor.plugin(target); } // return target; }
-
PluginAll () is a simple method that loops through plugin.wrap (target, this), so look at the wrap() method: plugin.wrap (target, this)
Public static Object wrap(Object target, Interceptor Interceptor) {// Get the @signature definition Map<Class, Set> signatureMap = getSignatureMap(interceptor); // Class type = target.getClass(); Class<? >[] interfaces = getAllInterfaces(type, signatureMap); If (interfaces.length > 0) {return proxy.newproxyInstance (type.getclassloader (), interfaces, new Plugin(target, interceptor, signatureMap)); } return target; }
-
Plugin.wrap() is a simple method to check whether the Plugin intercepts the corresponding component. If it intercepts the corresponding component, the Plugin returns the generated proxy object (Plugin).
How to enforce it?
-
Mybatis starts with a Plugin to generate a proxy object (Plugin). Now how does this proxy object perform?
-
Invoke (), invoke(), invoke(), invoke(), invoke()
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {Set methods = signaturemap.get (method.getDeclaringClass()); // If (methods! Return interceptor. Intercept (New Invocation(target, Interceptor)) {return interceptor. Intercept (New Invocation(Target, Invocation)); method, args)); Return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); }}
-
The logic is simple: if this method is intercepted, the plug-in’s intercept() method is executed; if not, the original method is executed.
-
Let’s look at the process again with the above custom plug-in:
-
The setParameters() method is called in PreparedStatementHandler, as shown below:
- The invoke() method is executed and the setParameters() method is intercepted, so the Intercept () method is executed directly.
conclusion
- The principle of Mybatis plug-in is actually very simple, divided into the following steps:
- Determine whether the component was intercepted at project startup, and if not return the original object directly.
- If any are intercepted, return the object of the dynamic proxy (Plugin).
- If the method is not a proxy object, execute the original method directly
- If it is a proxy object, invoke() of Plugin is invoked.
Principle analysis of paging plug-in
-
Maven uses pageHelper, a pagination plugin, as follows:
< the dependency > < groupId > com. Making. Pagehelper < / groupId > < artifactId > pagehelper < / artifactId > < version > 5.1.6 < / version > </dependency>Copy the code
-
The pagerinterceptor plugin is a pagerinterceptor plugin that provides a pagerinterceptor plugin.
@Intercepts( { @Signature(type = Executor.class, method = “query”, args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = “query”, args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), } ) public class PageInterceptor implements Interceptor {}
-
Since this is paging, it must be intercepted at query(), and therefore must be in the Executor component.
-
Instead of analyzing the source code, the pagination plugin reassigns RowBounds to the pagination data that you define for yourself. The pagination plugin is not the focus of this chapter, but rather the GitHub documentation.
The last
Thank you for reading here, the article is inadequate, welcome to point out; If you think it’s good, give me a thumbs up.