1.简介
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 的发行包中的源代码。 假设你想做的不仅仅是监控方法的调用,那么你应该很好的了解正在重写的方法的行为。 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。 这些都是更低层的类和方法,所以使用插件的时候要特别当心。
1.1实现
1)实现 Interceptor 接口,通过@Intercepts指定拦截位置
@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) {
}
}
2)配置mybatis-config.xml
<plugins> <plugin interceptor="org.mybatis.example.ExamplePlugin"> <property name="someProperty" value="100"/> </plugin> </plugins>
2.实例
对StatementHandler.query方法进行拦截,记录慢SQL
package com.siyuan.dao.mybatis;
import java.sql.Statement;
import java.util.Properties;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.log4j.Logger;
@Intercepts(@Signature(type = StatementHandler.class,
method = "query",
args = {Statement.class, ResultHandler.class }))
public class SqlStatisticInterceptor implements Interceptor {
private static final Logger LOGGER =
Logger.getLogger(SqlStatisticInterceptor.class);
// 慢SQL阀值
private int slowSQLThreshold = 3000;
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long timeSpent = System.currentTimeMillis() - start;
StatementHandler handler = (StatementHandler) invocation.getTarget();
String sql = handler.getBoundSql().getSql().trim();
LOGGER.debug("spent [" + timeSpent + "]ms for :\n" + sql);
if (timeSpent >= slowSQLThreshold) {
LOGGER.warn("slow sql which costs [" + timeSpent
+ "]ms has been found:\n" + sql);
}
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
if (properties.get("slowSQLThreshold") == null) {
return;
}
this.slowSQLThreshold =
Integer.parseInt((String) properties.get("slowSQLThreshold"));
}
}
2) mybatis-config.xml
<plugin interceptor="com.qfang.dao.mybatis.SqlStatisticInterceptor"> <property name="slowSQLThreshold" value="1000"/> </plugin>
3.原理
1)Interceptor.plugin方法将在创建Executor,ParameterHandler,ResultSetHandler,StatementHandler实例时被调用替代mybatis框架默认创建的实例
2)Plugin实现InvocationHandler,wrap方法通过Proxy创建Executor,ParameterHandler,ResultSetHandler,StatementHandler实例的代理类
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;
}
4.参考资料
http://www.mybatis.org/mybatis-3/zh/configuration.html#plugins