java MyBatis interceptor Inteceptor

  • 2020-05-16 07:04:55
  • OfStack

There are many beginners of java who are not familiar with MyBatis interceptor Inteceptor. Here I will put together the next article for you about MyBatis interceptor Inteceptor in java.

This paper mainly analyzes the plug-in mechanism of MyBatis, which is actually the responsibility chain pattern implementation of Java dynamic proxy.

According to official documents. Mybatis only allows the following methods to be intercepted, and this decision writes the interceptor annotation signature parameter.

The following code


Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)

The source code for interception is as follows, where interceptorChain.pluginAll (..) Is to weave in a custom interceptor:

The following code


/* org.apache.ibatis.session.Configuration Class method  */
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 following code


/*  Annotations indicate which interface method to intercept and its parameters  */
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class YourInterceptor implements Interceptor{
 public Object intercept(Invocation invocation) throws Throwable{
  doSomeThing();
  /*  Note: actually used here Invocation.proceed() Methods to complete interceptorChain The traversal call of the chain ( That is, all registrations are performed Interceptor the intercept methods ) , to eventually be invoked by the original method of the proxy object  */
  return invocation.proceed();
 }
  /* Generate paired targets target The agent of, and @Intercepts The note is in Plugin.wrap Used in the */
 @Override
 public Object plugin(Object target){
   /*  When the target class is StatementHandler Type when the target class is wrapped, not as a meaningless proxy  */
  return (target instanceof StatementHandler)?Plugin.wrap(target, this):target;
 }
  /* Use to set custom interceptor configuration parameters */
 @Override
 public void setProperties(Properties properties){
 }
}

Where, the code to intercept the call is in Plugin.wrap:

The following code


/* org.apache.ibatis.plugin.Plugin class  */
public class Plugin implements InvocationHandler {
 /*  Omit code ... */
 public static Object wrap(Object target, Interceptor interceptor) {
  /*  This is called fetch Interceptor Annotated signature  */
  Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
  Class<?> type = target.getClass();
  /*  Gets the interface that matches the interceptor target class  */
  Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
  if (interfaces.length > 0) {
   /*  use jdk A dynamic proxy  */
   return Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap));
  }
  return target;
 }
 /*  The execution of all methods of the intercepting target class will now be 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 code ... */
}

As you can see, the core code of the MyBatis interceptor design is still relatively simple, but flexible enough. In practice, be careful not to act as meaningless agents (Plugin.wrap).


Related articles: