现在有这样一个需求: 需要自定义mybatis插件判断当前sql执行时间时候超过自定义的值,如果超时就打印相应的日志
- 插件自定义代码:
github代码地址
@Intercepts(
{
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
@Signature(type = Executor.class, method = "queryCursor", args = {MappedStatement.class, Object.class, RowBounds.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
}
)
public class ConsumeTimeInterceptor implements Interceptor {
public static Log log = LogFactory.getLog(ConsumeTimeInterceptor.class);
long limitMilliSecond;
@Override
public Object intercept(Invocation invocation) throws Throwable {
long start = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
try {
return invocation.proceed();
} finally {
try {
long end = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
String methodName = invocation.getMethod().getName();
long consumeTime = end - start;
if (consumeTime > limitMilliSecond) {
String logString;
if ("update".equalsIgnoreCase(methodName)) {
logString = "slow update consume milliSecond: ";
} else {
logString = "slow query consume milliSecond: ";
}
log.debug(logString + consumeTime);
}
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
@Override
public void setProperties(Properties properties) {
String limitMilliSecond = properties.getProperty("limitMilliSecond");
if (limitMilliSecond != null) {
this.limitMilliSecond = Long.parseLong(limitMilliSecond);
}
}
}
通过实现Interceptor接口,就可以实现一个自定义插件
- 在mybatis配置文件配置插件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<plugins>
<plugin interceptor="io.github.mybatis.pal.ConsumeTimeInterceptor">
<property name="limitMilliSecond" value="10"/>
</plugin>
</plugins>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="" value="" />
</transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:basetest" />
<property name="username" value="sa" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="io.github.mybatis.pal.Mapper" />
</mappers>
</configuration>
- 我们看一下源码,看看插件是如何被设置生效的
DefaultSqlSessionFactory中的方法
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
configuration.newExecutor()方法如下:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
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;
}
interceptorChain生成:在项目启动,configuration被初始化,读取配置文件生成
这里我们看到作用于executor的并不是单个插件,是一个插件链,意味着我们可以定义多个插件,也可能我们引入的其他插件也会作用于executor
比如:如果我们实际项目中引入了分页插件,此时分页插件也会在此处作用于executor,从而达到分页的目的
这里设置插件使用了责任链模式,返回的是一个动态代理类,使用了代理模式,具体可以看mybatis源码
如果想要自己实现一个插件,可以上github下载代码,依葫芦画瓢
下一篇文章,我们将学习如何在springboot项目中引入该插件,实现自动化配置插件,直接引入maven依赖包就可以使用,无需手动配置
|