mybatis插件原理初始化过程

插件的目的

让程序员能对SQL参数设置到SQL执行结果返回过程进行扩展, 比如分页插件就是通过对StatementHandler.prepare方法进行拦截,将分页信息拼接到SQL上

原理

利用动态代理对拦截器进行包装,从而实现对所需方法的拦截处理

mybatis插件针对哪几个接口

Executor

执行器,对SqlSession暴露增删改查接口

ParameterHandler

参数处理器,对预编译占位参数进行设值

StatementHandler

SQL执行器,获取statement发送sql执行

ResultHandler

对结果集进行处理

四个接口执行流程

graph TB A[客户端] -->B[SqlSessionFactory] B-->C[SqlSession] C-->D[Executor] D-->E[ParameterHandler] E-->F[StatementHandler] F-->H[ResultHandler] H-->I[JDBC驱动] I-->C

插件如何加载

graph TB A[配置文件配置的Interceptor] --加载到拦截器链中-->B[Configuration.InterceptorChain] B-->C[Executor] C--InterceptorChain.pluginAll方法-->C2[生成代理对象Executor链] B-->D[ParameterHandler] D--InterceptorChain.pluginAll方法-->D2[生成代理对象ParameterHandler链] B-->E[StatementHandler] E--InterceptorChain.pluginAll方法-->E2[生成代理对象StatementHandler链] B-->F[ResultHandler] F--InterceptorChain.pluginAll方法-->F2[生成代理对象ResultHandler链]

如何实现一个插件

用分页插件举个例子

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PaginationInterceptor extends AbstractSqlParserHandler implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
    ......省略
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        //内部根据不同的数据拼接指定的分页SQL
        DialectModel model = DialectFactory.buildPaginationSql(page, buildSql, dbType, dialectClazz);
        Configuration configuration = mappedStatement.getConfiguration();
        List<ParameterMapping> mappings = new ArrayList<>(boundSql.getParameterMappings());
        Map<String, Object> additionalParameters = (Map<String, Object>) metaObject.getValue("delegate.boundSql.additionalParameters");
        model.consumers(mappings, configuration, additionalParameters);
        metaObject.setValue("delegate.boundSql.sql", model.getDialectSql());
        metaObject.setValue("delegate.boundSql.parameterMappings", mappings);
        return invocation.proceed();
    }

}

核心注解

@Intercepts

标注此类是一个拦截器

@Signature

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
  /**
   * 拦截的class类型
   *
   * @return the java type
   */
  Class<?> type();

  /**
   * 拦截的class中的方法名
   *
   * @return the method name
   */
  String method();

  /**
   * 方法所需的参数
   * @return java types for method argument
   */
  Class<?>[] args();
}
posted @ 2022-08-12 17:58  四码难追  阅读(48)  评论(0)    收藏  举报