1.Mybatis插件原理
Mybatis的插件,是采用责任链机制,通过JDK动态代理来实现的。默认情况下,Mybatis允许使用插件来拦截四个对象:
- Executor:执行CURD操作;
- StatementHandler:处理sql语句预编译,设置参数等相关工作;
- ParameterHandler:设置预编译参数用的;
- ResultSetHandler:处理结果集。
这个我们可以从Mybatis的源码中看到,例如下面创建Executor的时候,就是返回了一个代理Executor对象:
开始进入源码环节。
下面是InterceptorChain 类的源码:可以看到内部有一个拦截器链,调用pluginAll方法时,会遍历所有的拦截器的plugin方法
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);
}
}
查看 Interceptor源码:它是一个接口,其plugin方法又调用了Plugin.wrap方法,继续跟入。
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
}
}
Plugin源码:进到这里,思路就豁然开朗了,wrap方法里就是通过JDK动态代理创建了一个代理对象。下面我们逐行看下,都经过了哪些步骤。
/**
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.plugin;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.ibatis.reflection.ExceptionUtil;
/**
* @author Clinton Begin
*/
public class Plugin implements InvocationHandler {
private final Object target;
private final Interceptor interceptor;
private final Map<Class<?>, Set<Method>> signatureMap;
private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
this.target = target;
this.interceptor = interceptor;
this.signatureMap = signatureMap;
}
public static Object wrap(Object target, Interceptor interceptor) {
// 得到拦截器上的注解的 classType、 method、parameter参数的Map
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
// 获取被代理的Class类型
Class<?> type = target.getClass();
// 找到signatureMap中包含的 type 的所有父类接口
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 创建一个代理对象
return Proxy.newProxyInstance(
typ