MyBatis插件
插件概述
MyBatis允许用户自定义拦截器允许拦截:
- Executor类里的update() query() commit()等方法
- ParameterHanlder类里的getParameterObject() setParameters()等方法
- ResultSetHandler类里的handleResultSets()、handleOutputParameters()等方法
- StatementHandler类里的prepare()、query()等方法。
实现用户自定义的一些功能,对sql、参数、执行结果做其他处理,比如分页功能。
如何使用
继承Interceptor接口
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class MapUnderScoreToCamelCasePlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object o = invocation.proceed();
return o;
}
}
配置插件
<plugins>
<plugin interceptor="com.sss.plugins.paging.PageInterceptor">
<property name="dbType" value="mysql"/>
<property name="dialect.oracle" value="com.sss.plugins.paging.OracleDialect"/>
<property name="dialect.mysql" value="com.sss.plugins.paging.MySQLDialect"/>
</plugin>
<plugin interceptor="com.sss.plugins.ShowSqlPlugin"></plugin>
<plugin interceptor="com.sss.plugins.MapUnderScoreToCamelCasePlugin"></plugin>
</plugins>
模拟MyBatis插件的原理
Interceptor
自定义的Plugin要实现Interceptor接口,其中intercept()方法要在动态代理的invoke()方法中调用,出发自定义Plugin中的业务逻辑。
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}
InterceptorChain
该类将目标Target对象,循环包装,调用Plugins.wrap()方法生成动态代理对象。
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
Invocation
Invocation对象暴露给Plugin类,共插件拿到需要的参数自己做处理。
public class Invocation {
private final Object target;
private final Method method;
private final Object[] args;
public Invocation(Object target, Method method, Object[] args) {
this.target = target;
this.method = method;
this.args = args;
}
public Object getTarget() {
return target;
}
public Method getMethod() {
return method;
}
public Object[] getArgs() {
return args;
}
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}
Plugin
Plugin类里实现了动态代理的invoke方法。
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private Plugin(Object target, Interceptor interceptor) {
this.target = target;
this.interceptor = interceptor;
}
public static Object wrap(Object target, Interceptor interceptor) {
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor));
}
return target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return interceptor.intercept(new Invocation(target, method, args));
}
private static Class<?>[] getAllInterfaces(Class<?> type) {
Set<Class<?>> interfaces = new HashSet<>();
while (type != null) {
for (Class<?> c : type.getInterfaces()) {
interfaces.add(c);
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class<?>[interfaces.size()]);
}
}
调用方
public class Test {
public static void main(String[] args) {
InterceptorChain interceptorChain = new InterceptorChain();
interceptorChain.addInterceptor(new Plugin1());
interceptorChain.addInterceptor(new Plugin2());
Executor e = (Executor) interceptorChain.pluginAll(new SimpleExecutor());
e.query();
}
}
interface Executor{
void query();
}
class SimpleExecutor implements Executor{
public void query(){
System.out.println("做一些查询");
}
}
class Plugin1 implements Interceptor{
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("插件一拦截");
return invocation.proceed();
}
}
class Plugin2 implements Interceptor{
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("插件二拦截");
return invocation.proceed();
}
}
运行结果
debug可以看到:
最后拿到的Executor e其实是对目标target(SimpleExecutor类)嵌套了2层动态代理。