MyBatis 插件可以用来扩展/定制 MyBatis 核心的功能,可以对一些核心接口方法进行拦截和增强。插件需要实现 Interceptor 接口,并且在 MyBatis 配置文件中注册。一个插件可以通过动态代理或者反射的方式来改变 MyBatis 的行为,使得插件使用者可以在不修改 Mybatis 源代码的情况下,自定义一些特殊的逻辑处理(比如拦截 SQL 语句并输出到日志文件里面)。
插件运行流程:
1、创建 Executor ->创建ParameterHandler、ResultSetHandler、StatementHandler
2、如果设置了 plugin,就依次调用 interceptor.plugin(parameterHandler / resultsetHandler / statementHandler)方法,返回结果是最终被改变过的 parameterHandler / resultsetHandler / statementHandler
3、创建的 SqlSession 是使用上述改变过的 ParameterHandler、ResultSetHandler、StatementHandler
在执行查询操作的时候,MyBatis 会按照以下顺序执行:
1、先使用com.ibatis.sqlmap.engine.executor.statement.BaseStatementHandler#prepare(Statement statement)函数准备好 SQL,再交由 org.apache.ibatis.executor.Executor#query(Statement statement, ResultHandler resultHandler)执行该 SQL
2、如果用户配置了 Plugin,则在 BaseStatementHandler 对象生成时,会把 BaseStatementHandler 对象拦截下来通过调用 Plugin#interceptInvocation 返回新的 BaseStatementHandler 对象交给 Query.execute 方法执行,而这个 Query 对象实际的执行者是第一步中org.apache.ibatis.session.defaults.DefaultSqlSession#selectList
3、Plugin#interceptorInterceptor 可以简单理解为提供了类似 AOP 的拦截功能,在这个函数内可以自定义对原有 SQL 语句的处理
插件的开发和使用相对比较简单,只需要实现 Interceptor 接口,完成自己的逻辑并注册到 Configuration 对象即可。下面是一个简单的日志插件示例:
@Intercepts({@Signature(type = Executor.class, method =”query”, args ={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = StatementHandler.class, method =”prepare”, args ={Connection.class, Integer.class})})public class LoggingPlugin implements Interceptor {@Override public Object intercept(Invocation invocation) throws Throwable { long startTime = System.currentTimeMillis(); Object result = invocation.proceed(); if (invocation.getTarget() instanceof PreparedStatementHandler){ BoundSql boundSql =((PreparedStatementHandler) invocation.getTarget()).getBoundSql(); System.out.println(“SQL:”+ boundSql.getSql());} else if (invocation.getTarget() instanceof CallableStatementHandler){ //…} long endTime = System.currentTimeMillis(); System.out.println(“Time:”+ (endTime – startTime)+ “ms”); return result;} @Override public Object plugin(Object target){ return Plugin.wrap(target, this);} @Override public void setProperties(Properties properties){ //…}}
这个插件会在 MyBatis 执行 SQL 语句时打印出 SQL,以及执行时间的日志。可以在 MyBatis 的配置文件里面注册该插件:
上面插件开发示例中,还有另外一个 StatementHandler 拦截器,它是用于拦截更底层的 jdbc Statement 对象的。如果需要对底层对象进行操作,就需要在插件的签名中声明 StatementHandler 接口,这样才能获取到 Statement 对象并进行相关读写操作。
Mybatis 插件机制利用了代理模式,能够让我们不改变框架源代码基础上方便的对 Mybatis 核心功能进行增强或者定制化。但是,在使用 Mybatis 插件时也要注意灵活性和可扩展性,不要过分依赖插件去处理业务逻辑。
