Mybatis的核心实现Plugin、Interceptor

本章讲Interceptor其实在分享怎样的设计才是具备良好的拓展性的程序,注重内功修为的童靴可能更感兴趣。Mybatis中的插件允许你针对核心组件接口Executor 、StatementHandler、ParameterHandler、ResultSetHandler中任何一个方法进行拦截调用。而每个Interceptor(拦截的接口)其实是通过JDK的动态代理技术生成的代理类,每当执行这4种接口中的方法时,就会进入拦截方法(具体就是InvocationHandler的invoke()方法)。

关于Mybatis的系统架构及Executor、ParameterHandler、ResultSetHandler、StatementHandler可参阅之前的相关的分享

责任链模式

责任链模式是一种对象行为模式,插件用的是它,很多对象由每一个对象对其下家的引用而连接起来形成一条链,请求在这个链上传递,直到链上的某一个对象决定处理此请求。

用法

用法一点也不复杂:只需实现 Interceptor 接口,并指定想要拦截的方法签名即可,更多用法可参阅官方教程

除了用插件来修改 MyBatis 核心行为之外,还可以通过完全覆盖配置类来达到目的。只需继承后覆盖其中的每个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。再次重申,这可能会严重影响 MyBatis 的行为,务请慎之又慎。

<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>
//执行Executor.update时拦截
@Intercepts({@Signature(type= Executor.class, method = "update", args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  public Object intercept(Invocation invocation) throws Throwable {//干你想干的事情
    return invocation.proceed();
  }
  public Object plugin(Object target) {
    return Plugin.wrap(target, this);
  }
  public void setProperties(Properties properties) {
  }
}

源码

在上面的源码中Plugin.wrap(),它就是当前拦截器(ExamplePlugin)的代理类。而InterceptorChain.pluginAll()则定义了一个组件接口(比如:ParameterHandler)执行的责任链。

public interface Interceptor {
  //拦截器具体实现
  Object intercept(Invocation invocation) throws Throwable;
  //拦截器的代理类
  Object plugin(Object target);
  //添加属性
  void setProperties(Properties properties);
}
//源码Plugin.wrap其实是一个代理
public class Plugin implements InvocationHandler {
  private Object target;
  private Interceptor interceptor;//拦截器
  private Map<Class<?>, Set<Method>> signatureMap;
  //为拦截器生成代理类 
  public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> 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;//原生类返回
  }
  //代理类执行入口
  @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)) {//执行插件类的拦截规则
        return interceptor.intercept(new Invocation(target, method, args));
      }
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }
  //处理拦截器类上的各种注解
  private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    // issue #251
    if (interceptsAnnotation == null) {
      throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());      
    }
    Signature[] sigs = interceptsAnnotation.value();
    Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
    for (Signature sig : sigs) {
      Set<Method> methods = signatureMap.get(sig.type());
      if (methods == null) {
        methods = new HashSet<Method>();
        signatureMap.put(sig.type(), methods);
      }
      try {
        Method method = sig.type().getMethod(sig.method(), sig.args());
        methods.add(method);
      } catch (NoSuchMethodException e) {
        throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
      }
    }
    return signatureMap;
  }
}
//拦截器的请求
public class Invocation {
  private Object target;
  private Method method;
  private Object[] args;
  //调用原先真实方法(非代理)
  public Object proceed() throws InvocationTargetException, IllegalAccessException {
    return method.invoke(target, args);
  }
}

拦截器链执行过程

拦截器代理类对象->拦截器->目标方法,即:InvocationHandler.invoke()->Interceptor.intercept()->Invocation.proceed()。

也就是说其实在执行每个Executor、ParameterHandler、ResultSetHandler、StatementHandler都会构建一个拦截器链,如果针对每个配置了plugin,一个sql执行下来会陆续经过四个拦截器链。

//拦截器管理类
public class InterceptorChain {
  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
  //构建责任链
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {//存在拦截器则构建
      //有多个同类型的拦截器则构成链,将代理类作为target再成代理类
      target = interceptor.plugin(target);
    }
    return target;//可以没有拦截器
  }
  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }
  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }
}
//重要的配置类
public class Configuration {
  protected final InterceptorChain interceptorChain = new InterceptorChain();
  //有匹配的拦截器则构建责任链,没有则不生成
  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }
  //同newParameterHandler
  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);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }
  //同newParameterHandler
  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);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

  public Executor newExecutor(Transaction transaction) {
    return newExecutor(transaction, defaultExecutorType);
  }
  //同newParameterHandler
  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);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
}
//负责解析Mybatis 全局配置文件
public class XMLConfigBuilder extends BaseBuilder {
  private void pluginElement(XNode parent) throws Exception {//解析interceptor
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        interceptorInstance.setProperties(properties);
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }
}
public class SimpleExecutor extends BaseExecutor {
  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      //生成责任链
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);//可拦截query()
    } finally {
      closeStatement(stmt);
    }
  }
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());//可拦截prepare()
    handler.parameterize(stmt);//可拦截parameterize()
    return stmt;
  }
}

mybatis用拦截器的设计非常巧妙的利用了jdk的动态代理,其实复杂度并不高。

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
MyBatis Interceptor is a mechanism provided by MyBatis to intercept and modify SQL statements, results, and other operations executed by MyBatis. It is a powerful feature that can be used to add custom logic to MyBatis, such as logging, performance monitoring, security checks, and more. The MyBatis Interceptor works by intercepting method calls to the MyBatis core classes, such as SqlSession, MapperProxy, and Executor. It provides pre-processing and post-processing hooks for SQL statements, results, and exceptions. To use the MyBatis Interceptor, you need to create a class that implements the Interceptor interface and override its three methods: intercept(), plugin(), and setProperties(). The intercept() method is called for each method invocation, and you can modify the method arguments, invoke the method yourself, and modify the return value. The plugin() method is used to wrap the target object with the interceptor, and the setProperties() method is used to configure the interceptor with any properties it needs. Once you have implemented your interceptor, you need to register it with MyBatis by adding it to the configuration file, as follows: ``` <plugins> <plugin interceptor="com.example.MyInterceptor"> <property name="property1" value="value1"/> <property name="property2" value="value2"/> </plugin> </plugins> ``` In this example, the MyInterceptor class is registered as a plugin, and two properties are passed to it. Overall, the MyBatis Interceptor is a flexible and powerful tool that can be used to customize MyBatis behavior to fit your specific needs.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值