There are many java beginners who don’t know much about MyBatis interceptor Inteceptor. Here I will organize the next article for you to explain the MyBatis interceptor Inteceptor in java.
This article mainly analyzes the plug-in mechanism of MyBatis, which is actually the responsibility chain model implementation of Java dynamic proxy.
According to official documentation. Mybatis only allows intercepting the following methods, which determines the signature parameters of the interceptor annotation.
The code is as follows
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClose)ParameterHandler (getParameterObject, setParameters)ResultSetHandler (handleResultSets, handleOutputParameters)StatementHandler (prepare, parameterize, batch, update, query)
The source code of the interceptor processing is as follows, where interceptorChain.pluginAll(..) is a custom interceptor weaving:
The code is as follows
/* Method in org.apache.ibatis.session.Configuration class */public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); /* Intercept ParameterHandler*/ parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); return parameterHandler;}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); /* Intercept ResultSetHandler*/ 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); /* Intercept StatementHandler*/ statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler;}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); } /* Intercept Executor*/ executor = (Executor) interceptorChain.pluginAll(executor); return executor;} To implement a custom interceptor, you only need to implement the Interceptor interface. The general code is as follows:
The code is as follows
/* The method and parameters of the annotation that indicate which interface to intercept*/@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) }) public class YourInterceptor implements Interceptor{ public Object intercept(Invocation invocation) throws Throwable{ doSomeThing(); /* Note: Here, the Invocation.proceed() method is actually used to complete the traversal call of the interceptorChain chain (that is, to execute all registered Intercept methods of the Interceptor), and to the original method call of the final proxy object */ return invocation.proceed(); } /* Generate a proxy to the target target, and the annotation of @Intercepts is used in Plugin.wrap */ @Override public Object plugin(Object target){ /* When the target class is of StatementHandler type, the target class is wrapped and no meaningless proxy is used */ return (target instanceof StatementHandler)?Plugin.wrap(target, this):target; } /* Used to set custom interceptor configuration parameters*/ @Override public void setProperties(Properties properties){ }} Among them, the code for intercepting calls is in Plugin.wrap:
The code is as follows
/* org.apache.ibatis.plugin.Plugin class*/public class Plugin implements InvocationHandler { /* Omit the code... */ public static Object wrap(Object target, Interceptor interceptor) { /* Here is to get the annotation signature of the Interceptor*/ Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor); Class<?> type = target.getClass(); /* Get the interface matching the target class*/ Class<?>[] interfaces = getAllInterfaces(type, signatureMap); if (interfaces.length > 0) { /* Use jdk dynamic proxy*/ return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)); } return target; } /* The execution of all methods of the intercept target class will become executed here*/ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Set<Method> methods = signatureMap.get(method.getDeclaringClass()); if (methods != null && methods.contains(method)) { /* Execute the interceptor method*/ return interceptor.intercept(new Invocation(target, method, args)); } return method.invoke(target, args); } catch (Exception e) { throw ExceptionUtil.unwrapThrowable(e); } } /* Omit the code... */}You can see that the core code of MyBatis interceptor design is relatively simple, but it is flexible enough. When using it in practice, be careful not to be meaningless proxy (Plugin.wrap).