通过前文了解到 真正执行的Executor是一个invocationHandler为包装了Interceptor的Plugin的代理类!
Plugin源码如下:
想通过自定义sql执行拦截器来统一打印sql的执行耗时。
配置sqlSessionFactory增加Interceptor:
@Bean(name = "sqlSessionFactory")
private SqlSessionFactoryBean sqlSessionFactory(TDataSource mysqlTddlDataSource) throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(mysqlTddlDataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:mapper/*Mapper.xml"));
sqlSessionFactoryBean.setPlugins(new Interceptor[] { new SqlStatementInterceptor(ServiceNameConstant.ENGINE)});
return sqlSessionFactoryBean;
}
拦截器: @Signature标识哪样的类在什么样的方法上进行切面。 详见: mybatis(6) - 自定义拦截器
package com.noob.interceptor;
import java.util.Properties;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
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.ibatis.session.RowBounds;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.noob.common.constant.CommonConstants;
import com.noob.common.constant.CommonConstants.MonitorLogKey;
import com.noob.util.CommonUtil;
import com.noob.util.NetUtil;
/**
* 数据库操作性能拦截器,记录耗时
*/
@Intercepts(value = {
@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }),
@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
RowBounds.class, ResultHandler.class }) })
@NoArgsConstructor
@Slf4j
public class SqlStatementInterceptor implements Interceptor {
private Properties properties;
private String hostIp;
public SqlStatementInterceptor(String appName) {
properties = new Properties();
properties.setProperty(MonitorLogKey.PROJECT_NAME, appName);
hostIp = NetUtil.getLocalAddress();
}
@Override
public Object intercept(Invocation arg0) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) arg0.getArgs()[0];
String sqlId = mappedStatement.getId();
Object returnValue;
long start = System.currentTimeMillis();
returnValue = arg0.proceed();
long end = System.currentTimeMillis();
log(sqlId, end - start);
return returnValue;
}
private void log(String sqlId, long costTime) {
try {
JSONObject logObj = CommonUtil.createGrafanaLog();
logObj.put(MonitorLogKey.LOG_TYPE, CommonConstants.MonitorType.SQL_MONITOR);//sql 耗时
logObj.put(MonitorLogKey.PROJECT_NAME, properties.get(MonitorLogKey.PROJECT_NAME));
logObj.put(MonitorLogKey.HOST_IP, hostIp);
logObj.put("sql", sqlId);
logObj.put("costTime", costTime);
log.info(JSONObject.toJSONString(logObj, SerializerFeature.UseISO8601DateFormat));
} catch (Exception e) {
log.warn("output sql costTime fail.", e);
}
}
@Override
public Object plugin(Object arg0) {
return Plugin.wrap(arg0, this);
}
@Override
public void setProperties(Properties arg0) {
this.properties = arg0;
}
}