1、前言
简单的说,mybatis插件就是对ParameterHandler、ResultSetHandler、StatementHandler、Executor这四个接口上的方法进行拦截,利用JDK动态代理机制,为这些接口的实现类创建代理对象,在执行方法时,先去执行代理对象的方法,从而执行自己编写的拦截逻辑,所以真正要用好mybatis插件,主要还是要熟悉这四个接口的方法以及这些方法上的参数的含义;
另外,如果配置了多个拦截器的话,会出现层层代理的情况,即代理对象代理了另外一个代理对象,形成一个代理链条,执行的时候,也是层层执行;
关于mybatis插件涉及到的设计模式和软件思想如下:
设计模式:代理模式、责任链模式;
软件思想:AOP编程思想,降低模块间的耦合度,使业务模块更加独立;
一些注意事项:
不要定义过多的插件,代理嵌套过多,执行方法的时候,比较耗性能;
拦截器实现类的intercept方法里最后不要忘了执行invocation.proceed()方法,否则多个拦截器情况下,执行链条会断掉;
2、Springboot 编写 mybatis 插件
编写 mybatis 插件很简单,首先定义要拦截的是上面说到的哪个类,拦截哪个方法,参数是啥,然后配置一下即可
package com.snowflake1.test.config;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.util.Properties;
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class MyPlugin implements Interceptor {
private Logger logger = LoggerFactory.getLogger(MyPlugin.class);
private long time;
//方法拦截
@Override
public Object intercept(Invocation invocation) throws Throwable {
//通过StatementHandler获取执行的sql
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
long start = System.currentTimeMillis();
Object proceed = invocation.proceed();
long end = System.currentTimeMillis();
if ((end - start) > time) {
logger.info("本次数据库操作是慢查询,sql是:" + sql);
}
return proceed;
}
//获取到拦截的对象,底层也是通过代理实现的,实际上是拿到一个目标代理对象
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
//获取设置的阈值等参数
@Override
public void setProperties(Properties properties) {
this.time = Long.parseLong(properties.getProperty("time"));
}
}
在 springboot 那配置一下(我用的是 mybatisplus)
package com.snowflake1.test.config;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class MapperConfig {
//将插件加入到mybatis插件拦截链中
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return new ConfigurationCustomizer() {
@Override
public void customize(MybatisConfiguration configuration) {
//插件拦截链采用了责任链模式,执行顺序和加入连接链的顺序有关
MyPlugin myPlugin = new MyPlugin();
//设置参数,比如阈值等,可以在配置文件中配置,这里直接写死便于测试
Properties properties = new Properties();
//这里设置慢查询阈值为1毫秒,便于测试
properties.setProperty("time", "1");
myPlugin.setProperties(properties);
configuration.addInterceptor(myPlugin);
}
};
}
}