mybatis(6) - 自定义拦截器: Interceptor & plugin

可以拦截哪些方法

默认情况下,Mybatis允许使用插件来拦截的类及方法有:

  • Executor:update、query、flushStatements、commit、rollback、getTransaction、close、isClosed。
    实现类:SimpleExecutor/BatchExecutor/ReuseExecutor/CachingExecutor
  • ParameterHandler:getParameterObject、setParameters。
    实现类:DefaultParameterHandler
  • ResultSetHandler:handleResultSets、handleOutputParameters。
    实现类:DefaultResultSetHandler
  • StatementHandler:prepare、parameterize、batch、update、query。
    实现类:CallableStatementHandler/PreparedStatementHandler/SimpleStatementHandler/RoutingStatementHandler

如何自定义插件

只需实现Interceptor接口,并指定要拦截的类、方法。
很重要的是实现的plugin方法,约定了如何包装生成这个代理类。本类其实类似于代理的invocationhandler。一般Plugin.wrap(target,this)就行了

在intercept方法中一定要调用Invocation的proceed方法并将返回值返回。

@Intercepts({
    @Signature(
        type=Executor.class,method="update",args={ MappedStatement.class,Object.class })
})
public class ExamplePlugin implements Interceptor {
    public Object intercept(Invocation invocation) throws Throwable {
       //自定义实现 do somethings 
       return invocation.proceed();
    }
    public Object plugin(Object target){
        return Plugin.wrap(target,this)
    }
    public void setProperties(Properties properties){
      //传入配置项
      String size = properties.getProperty("size");
    }
}
<!-- mybatis-config.xml -->
<plugins>
    <plugin interceptor="org.mybatis.example.ExamplePlugin">
        <!-- 这里的配置项就传入setProperties方法中 -->
        <property name="size" value="100">
    </plugin>
</plugins>

拦截器实现原理

如果了解Mybatis的拦截器实现原理,可以在以后的工作中也可使用该方法实现自己的拦截器

4849142-92de652a7922a460.png

插件包相关源码
//拦截器接口,供外部实现,实现该接口就定义了一个插件
public interface Interceptor {
  //拦截方法,可以将自定义逻辑写在该方法中
  Object intercept(Invocation invocation) throws Throwable;
  //包装成插件,一般Plugin.wrap(target,this)就行了
  Object plugin(Object target);
  //传入自定义配置参数
  void setProperties(Properties properties);
}
拦截器上定义的注解
@Intercepts:拦截器注解,包括一个或多个@Signature,拦截的目标类信息
@Signature:拦截的目标类信息,包括type、method、args,一个@Intercepts中可包含多个@Signature

public class Invocation {
  private Object target;//目标对象
  private Method method;//调用方法
  private Object[] args;//方法形参列表
  //省略get和set方法
  //执行调用,基于动态代理,在Interceptor的intercept方法中一定要调用该方法
  public Object proceed() throws InvocationTargetException, IllegalAccessException {
    return method.invoke(target, args);
  }
}

在配置文件中定义的过滤器,都保存在Configuration类的interceptorChain中,这个类保存了mybatis的所有配置,interceptorChain类中保存中所有Interceptor集合组成的拦截器链。

  //XMLConfigBuilder类中解析mybatis-config.xml 核心方法parseConfiguration(XNode root)
  pluginElement(root.evalNode("plugins"));//插件配置项

 private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
    //遍历 plugins的子节点plugin
    for (XNode child : parent.getChildren()) {
        String interceptor = child.getStringAttribute("interceptor");//获取interceptor属性值
        Properties properties = child.getChildrenAsProperties();//获取plugin属性值
        //创建拦截器实例,这里interceptor值也可以是typeAlias注册的简名
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        //设置属性项
        interceptorInstance.setProperties(properties);
        //添加到interceptorChain中
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }
  //Configuration类,添加拦截器
  public void addInterceptor(Interceptor interceptor) {
    interceptorChain.addInterceptor(interceptor);
  }

拦截的哪些接口

  //SQL语句处理器
  public interface StatementHandler {
    //预备工作
    Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
    //参数处理
    void parameterize(Statement statement) throws SQLException;
    //批量处理
    void batch(Statement statement)  throws SQLException;
    //更新处理
    int update(Statement statement) throws SQLException;
    //查询处理
    <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
  }

  //返回集处理器
  public interface ResultSetHandler {
    //处理返回结果
    <E> List<E> handleResultSets(Statement stmt) throws SQLException;
    //处理输出参数
    void handleOutputParameters(CallableStatement cs) throws SQLException;
  }

  //参数处理器
  public interface ParameterHandler {
     
    Object getParameterObject();

    void setParameters(PreparedStatement ps) throws SQLException;
  }

//执行器
public interface Executor {
    //更新
    int update(MappedStatement ms, Object parameter) throws SQLException;
    //查询(先查缓存)
    <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, 
          ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
    //查询
    <E> List<E> query(MappedStatement ms, Object parameter, 
          RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
    //查询游标
    <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) 
          throws SQLException;
    //刷新Statement
    List<BatchResult> flushStatements() throws SQLException;
    //提交事务
    void commit(boolean required) throws SQLException;
    //回滚事务
    void rollback(boolean required) throws SQLException;
    //创建缓存key
    CacheKey createCacheKey(MappedStatement ms, Object parameterObject,RowBounds rowBounds, 
          BoundSql boundSql);
    //是否存在key
    boolean isCached(MappedStatement ms, CacheKey key);
    //清除本地缓存
    void clearLocalCache();
    //延迟加载
    void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, 
          Class<?> targetType);
    //获取事务
    Transaction getTransaction();
    //关闭连接
    void close(boolean forceRollback);
    //是否关闭
    boolean isClosed();
    //设置Executor
    void setExecutorWrapper(Executor executor);
}

如何拦截这些接口

关键在于Configuration  在创建代理对象时执行的: interceptorChain.pluginAll方法。
最终回到了Plugin.wrap方法,会调用getAllInterfaces方法对signatureMap进行按@Signature的type进行筛选。有则构建代理对象,无则直接返回target源对象!
167c9f8133558109df04c1883845b0a1456.jpg

//创建相应Handler时会将所有拦截器通过动态代理方式返回代理Handler
public class Configuration {

  //创建ParameterHandler(参数处理器)
  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, 
        Object parameterObject, BoundSql boundSql) {
  // 根据指定Lang(默认RawLanguageDriver),创建ParameterHandler,将实际参数传递给JDBC语句
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(
        mappedStatement, parameterObject, boundSql);
    //返回代理实例
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }

  //创建ResultSetHandler(结果处理器)
  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, 
     RowBounds rowBounds,ParameterHandler parameterHandler,
     ResultHandler resultHandler,BoundSql boundSql) {
    //默认使用DefaultResultSetHandler创建ResultSetHandler实例
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement,
       parameterHandler, resultHandler, boundSql, rowBounds);
    //返回代理实例
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

  //创建StatementHandler(SQL语句处理器)
  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, 
    Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    //默认使用RoutingStatementHandler(路由作用)
    //创建指定StatementHandler实例(默认SimpleStatementHandler)
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, 
          parameterObject, rowBounds, resultHandler, boundSql);
    //返回代理实例
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

  //创建Executor(执行器)
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    //获取executorType,默认是SIMPLE
    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;
  }
}

Plugin源码如下:

//动态代理实现
public class Plugin implements InvocationHandler {
  private Object target;
  private Interceptor interceptor;//拦截器
  private Map<Class<?>, Set<Method>> signatureMap;//拦截目标类的目标方法

  private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
    this.target = target;
    this.interceptor = interceptor;
    this.signatureMap = signatureMap;
  }
  //包装目标实例
  public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    //目标类所有接口是否有signatureMap中定义的Class
    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);
    }
  }
  //获取拦截器上的SignatureMap
  private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
    Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
    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;
  }

  private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
    Set<Class<?>> interfaces = new HashSet<Class<?>>();
    while (type != null) {//获取type上的所有接口
      for (Class<?> c : type.getInterfaces()) {
        if (signatureMap.containsKey(c)) {//这里不判断Method,只判断Class<?>
          interfaces.add(c);
        }
      }
      type = type.getSuperclass();
    }
    return interfaces.toArray(new Class<?>[interfaces.size()]);
  }
}

总结

1.拦截器实现
Interceptor接口供插件实现,@Intercepts注解在插件实现上,表示这是一个插件类并配置将要拦截哪些类的哪些方法,@Signature定义将要拦截的方法信息,如类名、方法名、形参列表,Plugin类实现了InvocationHandler接口,是动态代理的具体实现,Invocation类包装了拦截的目标实例,InterceptorChain保存所有拦截器。
2.如何实现拦截
创建目标实例,比如A a = new A();
Interceptor interceptor = new LogInterceptor();
将A b = (A)interceptor.plugin(a); 这里b就是a的代理实例,在调用a中的save方法时,实际将调用interceptor的intercept方法,在该方法中一定要调用Invocation的proceed方法并将返回值返回。

 

转载于:https://my.oschina.net/u/3434392/blog/3010930

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值