經過前文了解到 真正執行的Executor是一個invocationHandler爲包裝了Interceptor的Plugin的代理類!java
Plugin源碼以下:mysql
配置sqlSessionFactory增長Interceptor:sql
@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(); //輸出sqlId 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; } }
輸出boundSql -> PreparedStatementapache
StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = statementHandler.getBoundSql(); String sql = boundSql.getSql(); sql = sql.replace("\n", "").replace("\t", "").replaceAll("\\s+", " ");
輸出真實執行的sql語句json
private void sqlLog(Invocation invocation) { try { Object firstArg = invocation.getArgs()[0]; Statement statement; if (Proxy.isProxyClass(firstArg.getClass())) { statement = (Statement) SystemMetaObject.forObject(firstArg).getValue("h.statement"); } else { statement = (Statement) firstArg; } MetaObject stmtMetaObj = SystemMetaObject.forObject(statement); try { statement = (Statement) stmtMetaObj.getValue("stmt.statement"); } catch (Exception e) { } if (stmtMetaObj.hasGetter("delegate")) { try { statement = (Statement) stmtMetaObj.getValue("delegate"); } catch (Exception e) { } } if (statement != null && StringUtils.isNotEmpty(statement.toString())) { String originalSql = statement.toString(); originalSql = originalSql.replaceAll("[\\s]+", " "); int index = this.indexOfSqlStart(originalSql); if (index > 0) { originalSql = originalSql.substring(index); } sqlLogger.info(originalSql + ";"); } } catch (Exception e) { log.error("sql log error", e); } } private int indexOfSqlStart(String sql) { String upperCaseSql = sql.toUpperCase(); Set<Integer> set = new HashSet<Integer>(); set.add(upperCaseSql.indexOf("SELECT ")); set.add(upperCaseSql.indexOf("UPDATE ")); set.add(upperCaseSql.indexOf("INSERT ")); set.add(upperCaseSql.indexOf("DELETE ")); set.remove(-1); if (CollectionUtils.isEmpty(set)) { return -1; } else { List<Integer> list = Lists.newArrayList(set); list.sort(Comparator.naturalOrder()); return (Integer)list.get(0); } }