延安网站制作,怎么把wordpress后台设置成中文,免费站推广网站在线,佛山教育平台网站建设MyBatis的自定义插件
前置知识
MyBatis 可以拦截的四大组件
Executor - 执行器StatementHandler - SQL 语句构造器ParameterHandler - 参数处理器ResultSetHandler - 结果集处理器
自定义 MyBatis 插件
/*** 打印 sql 执行的时间插件*/
Intercepts(// 指定拦截器拦截的对象…MyBatis的自定义插件
前置知识
MyBatis 可以拦截的四大组件
Executor - 执行器StatementHandler - SQL 语句构造器ParameterHandler - 参数处理器ResultSetHandler - 结果集处理器
自定义 MyBatis 插件
/*** 打印 sql 执行的时间插件*/
Intercepts(// 指定拦截器拦截的对象、方法和参数类型{Signature(type StatementHandler.class, method update, args {Statement.class}),Signature(type StatementHandler.class, method query, args {Statement.class, ResultHandler.class}),Signature(type StatementHandler.class, method batch, args {Statement.class})}
)
// 注册到 Spring 容器不是 Spring 环境的话可以用 mybatis 的 config 配置进去
Component
public class SqlExecuteTimePrintMybatisPlugin implements Interceptor {protected Logger logger LoggerFactory.getLogger(SqlExecuteTimePrintMybatisPlugin.class);Overridepublic Object intercept(Invocation invocation) throws Throwable {// 获取代理对象StatementHandler statementHandler (StatementHandler) invocation.getTarget();// 获取执行 sqlBoundSql boundSql statementHandler.getBoundSql();// 此处简单处理一下只打印参数替换前的 sql目的是演示自定义插件String sql boundSql.getSql();long start System.currentTimeMillis();try {return invocation.proceed();} finally {logger.info(sql - {}, takes time - {}, sql, System.currentTimeMillis() - start);}}
}效果如下
2023-10-14 17:18:39.297 INFO 25972 --- [p-nio-80-exec-1] c.y.m.c.SqlExecuteTimePrintMybatisPlugin : sql - SELECT * FROM INFO WHERE id ? , takes time - 57
2023-10-14 17:18:39.324 INFO 25972 --- [p-nio-80-exec-1] c.y.m.c.SqlExecuteTimePrintMybatisPlugin : sql - SELECT id, info_id, extend_info FROM INFO_DETAIL WHERE info_id ?, takes time - 4源码解析
创建四大对象的代码如下
public class Configuration {public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);// 此处增加拦截器责任链parameterHandler (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler;}public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,ResultHandler resultHandler, BoundSql boundSql) {ResultSetHandler resultSetHandler new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);// 此处增加拦截器责任链resultSetHandler (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);return resultSetHandler;}public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {StatementHandler statementHandler new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);// 此处增加拦截器责任链statementHandler (StatementHandler) interceptorChain.pluginAll(statementHandler);return statementHandler;}public Executor newExecutor(Transaction transaction) {return newExecutor(transaction, defaultExecutorType);}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;}
}首先在创建 Executor、StatementHandler、ParameterHandler、ResultSetHandler 四个对象时将插件plugins注入 调用 InterceptorChain.pluginAll() 方法将插件增加到责任链并返回代理后的 target 包装对象InterceptorChain 保存了所有的拦截器Interceptors 最终在执行的时候调用的其实是 JDK 动态代理的对象执行 MyBatis 中 InvocationHandler 的实现 org.apache.ibatis.plugin.Plugin 的 invoke 方法
public class InterceptorChain {private final ListInterceptor interceptors new ArrayList();public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target interceptor.plugin(target);}return target;}
}public interface Interceptor {// 拦截器增强方法Object intercept(Invocation invocation) throws Throwable;// 包装原来的对象default Object plugin(Object target) {return Plugin.wrap(target, this);}default void setProperties(Properties properties) {// NOP}}public class Plugin implements InvocationHandler {private final Object target;private final Interceptor interceptor;private final MapClass?, SetMethod signatureMap;private Plugin(Object target, Interceptor interceptor, MapClass?, SetMethod signatureMap) {this.target target;this.interceptor interceptor;this.signatureMap signatureMap;}public static Object wrap(Object target, Interceptor interceptor) {MapClass?, SetMethod signatureMap getSignatureMap(interceptor);Class? type target.getClass();Class?[] interfaces getAllInterfaces(type, signatureMap);if (interfaces.length 0) {// jdk 动态代理return Proxy.newProxyInstance(type.getClassLoader(),interfaces,new Plugin(target, interceptor, signatureMap));}return target;}Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {// 获取插件中生命要增强的方法SetMethod methods signatureMap.get(method.getDeclaringClass());// 如果命中该方法就使用执行插件中增强的方法if (methods ! null methods.contains(method)) {return interceptor.intercept(new Invocation(target, method, args));}// 没有命中就不对方法进行增强return method.invoke(target, args);} catch (Exception e) {throw ExceptionUtil.unwrapThrowable(e);}}...
}备注 不明白的需要去看下 JDK 动态代理实现原理概括的来讲就是 Proxy#newProxyInstance 时通过字节码增强的方法生成一个实现了跟被代理类相同接口并继承了 java.lang.reflect.Proxy 的类并返回其实例调用这个代理类的方法时实际上调用的是 Proxy.InvocationHandler.invoke(this, method, new Object[]{args}) 方法