Spring Boot 定義攔截器,攔截所有執行的sql
代碼實現
import com.alibaba.fastjson.JSONObject;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.Properties;
@Intercepts({@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
@Signature(type = StatementHandler.class, method = "batch", args = {Statement.class})})
@Component
@Configuration
public class SqlStatementInterceptor implements Interceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(SqlStatementInterceptor.class);
@Override
public Object intercept(Invocation invocation) {
// 開始時間
try {
Object target = invocation.getTarget();
System.out.println("-->"+ JSONObject.toJSONString(target));
Field delegate = target.getClass().getDeclaredField("delegate");
delegate.setAccessible(true);
Object delegateObj = delegate.get(target);
System.out.println("fff");
Field[] declaredFields = delegateObj.getClass().getSuperclass().getDeclaredFields();
Field parameterHandler = delegateObj.getClass().getSuperclass().getDeclaredField("boundSql");
parameterHandler.setAccessible(true);
Object parameterHandlerObj = parameterHandler.get(delegateObj);
Field parameterObject = parameterHandlerObj.getClass().getDeclaredField("parameterObject");
parameterObject.setAccessible(true);
Object o = parameterObject.get(parameterHandlerObj);
System.out.println("攔截到執行的sql:--->"+o);
}catch (Exception e){
e.printStackTrace();
}
long start = System.currentTimeMillis();
invocation.getArgs();
try {
return invocation.proceed();
} catch (Exception e) {
LOGGER.error("執行失敗!", e);
return null;
} finally {
long end = System.currentTimeMillis();
long time = end - start;
LOGGER.info("cost time {}ms", time);
}
}
@Override
public Object plugin(Object arg0) {
return Plugin.wrap(arg0, new SqlStatementInterceptor());
}
@Override
public void setProperties(Properties arg0) {
}
}
output:
攔截到執行的sql:--->select zni.id,zni.notice_title,zni.notice_content,zni.create_time,zni.update_time,bu.realname as create_user,buu.realname as update_user,dc.dict_value,
(select count(1)
from xxxx_file_info zfi
where zfi.outside_id = zni.id
and type = '0') as file_count
from xxxx_notice_info zni left join base_user bu on bu.userid=zni.create_user left join base_user buu on bu.userid=zni.update_user and bu.userid=buu.userid left join xxxx_DICT_AFTERLOAN@DICTIONARY dc on (dc.type_code='xxxx_dh_notice_status' and dc.dict_code=zni.notice_status ) where 1=1 order by is_top desc,create_time desc
一些說明
這裏配置了攔截器,攔截指定類中方法的執行,說白了就是標記,讓spring 生成代理類,然後每次調用調用都執行我們的方法
Intercepts註解用於定義要攔截的方法集合,Signature 註解定義要攔截的方法定義,type定義要攔截的類,method 定義方法名,args定義參數列表,定義好後spring 在啓動的時候會根據這個註解來生成代理類,這樣每次執行指定類裏面的方法就會被我們定義的攔截器攔截掉
比較重要的就是invocation對象的解析
這裏有點坑,
public class PreparedStatementHandler extends BaseStatementHandler {
public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
}
這個delegate字段是這個類型的,然後我們要獲得他的名字叫做boundSql的字段,然後這個字段在這個父類裏面
public abstract class BaseStatementHandler implements StatementHandler {
protected final Configuration configuration;
protected final ObjectFactory objectFactory;
protected final TypeHandlerRegistry typeHandlerRegistry;
protected final ResultSetHandler resultSetHandler;
protected final ParameterHandler parameterHandler;
protected final Executor executor;
protected final MappedStatement mappedStatement;
protected final RowBounds rowBounds;
protected BoundSql boundSql;
因此,這裏我們獲得這個boundSql字段要這樣
Field parameterHandler = delegateObj.getClass().getSuperclass().getDeclaredField("boundSql");
其他沒什麼要注意的了,主要就是參數列表對象的分析