默认情况下,Mybatis允许使用插件来拦截的接口和方法包括:
- Executor(update、query、flushStatements、commit、rollback、getTransaction、close、isClosed)是执行SQL的全过程,包括组装参数,组装结果集返回和执行SQL过程,都可以拦截
- ParameterHandler(getParameterObject、setParameters)执行SQL的参数组装,可以通过拦截它来重写组装参数规则
- ResultSetHandler(handleResultSets、handleCursorResultSets、handleOutputParameters)执行结果的组装,拦截它可以重写组装结果的规则
StatementHandler(prepare、parameterize、batch、update、query)是执行SQL的过程,拦截它可以重写执行SQL的过程
Mybatis插件可以通过实现接口Interceptro(org.apache.ibatis.plugin.Interceptor),在实现类中对拦截的对象和方法进行处理的方式进行实现。
该接口代码如下:
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
先看setProperties方法,它用来传递插件的参数,可以通过参数改变插件的行为。
<plugins>
<plugin interceptor="tk.mybatis.plugin.XXXXInterceptor">
<property name="pro1" value="value1" />
<property name="pro2" value="value2" />
</plugin>
</plugins>
setProperties方法会将上面的配置项读取出来,并传递给拦截器。在拦截器中可以通过Properties取得配置的参数值。
再看第二个方法plugin。参数target就是拦截器要拦截的对象,该方法会在创建被拦截的接口实现类时被调用。它通常实现起来简单,如下代码:
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
Plugin.wrap方法会自动判断拦截器的签名和被拦截对象的接口是否匹配,只有匹配的情况下才会使用动态代理拦截目标对象,因此上面的方法中不用做额外的逻辑判断。
最后一个intercept方法,它是Mybatis运行时要执行的拦截方法。
@Override
public Object intercept(Invocation invocation) throws Throwable {
//获取当前被拦截的对象
Object target = invocation.getTarget();
//获取当前被拦截的方法
Method method = invocation.getMethod();
//获取被拦截方法中的参数
Object[] args = invocation.getArgs();
//执行被拦截对象的被拦截的方法,并返回结果
Object result = invocation.proceed();
return result;
}
拦截器的签名:
除了要实现拦截器接口外,还需要给实现类配置注解。
@Intercepts(
@Signature(
type=Executor.class,
method="query",
args={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))
public class SimpleInterceptor implements Interceptor {}
@Intercepts(org.apache.ibatis.plugin.Intercepts)和签名注解@Signature(org.apache.ibatis.plugin.Signature)
@Signature注解包含三个属性:
- type:设置拦截器的接口(包括Executor、ParameterHandler、ResultSetHandler、StatementHandler)
- method:设置拦截器中的方法名,为上述四个接口中的方法
args:设置拦截方法的参数类型数组
但是并不是4个接口中所有的方法都可以被拦截的。下面是分别列举的可以被拦截的方法:
Executor接口:- int update(MappedStatement ms, Object parameter) throws SQLException;
- List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
- Cursor queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
- List flushStatements() throws SQLException;
- void commit(boolean required) throws SQLException;
- void rollback(boolean required) throws SQLException;
- Transaction getTransaction();
- void close(boolean forceRollback);
boolean isClosed();
ParameterHandler接口:
Object getParameterObject();
void setParameters(PreparedStatement ps)
throws SQLException;ResultSetHandler接口:
List handleResultSets(Statement stmt) throws SQLException;
Cursor handleCursorResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
StatementHandler接口:
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;void parameterize(Statement statement)
throws SQLException;int update(Statement statement)
throws SQLException;List query(Statement statement, ResultHandler resultHandler)
throws SQLException;Cursor queryCursor(Statement statement)
throws SQLException;- -