MyBatis插件源码解析
这里用到的设计模式可以参考一下这篇文章:https://www.cnblogs.com/qdhxhz/p/11390778.html,这个也是看到别人的文章理解写的,不过我觉得他借鉴的文章写的挺好的:https://www.jianshu.com/p/b82d0a95b2f3,可以照着敲一遍。
先理解用到的设计模式,就比较容易懂MyBatis插件了。
我们知道Mybatis插件本质上是一个拦截器,拦截的是Mybatis的四大对象
(1)ParameterHandler:处理SQL的参数对象
(2)ResultSetHandler:处理SQL的返回结果集
(3)StatementHandler:数据库的处理对象,用于执行SQL语句
(4)Executor:MyBatis的执行器,用于执行增删改查操作
/**
* @author Clinton Begin
*/
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;
}
//调用了这个方法,将拦截器对象添加到List<Interceptor> interceptors集合中
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
pluginAll方法中遍历了所有的拦截器,我们点击看看有哪些类使用了这个方法
可以看到调用pluginAll的类刚好是Mybatis拦截的的四大对象,由此可以判断拦截器生效的方法就是pluginAll方法。
pluginAll方法做了什么操作:
循环了List<Interceptor> interceptors
集合对象调用了一个interceptor.plugin(target);方法并返回了一个Object对象,我们看看interceptor.plugin(target)的源码是怎样的。
/**
* @author Clinton Begin
*/
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
点击 Object plugin(Object target);发现了我们自己写的插件类。
自己编写的插件类:CustomPlugin
/**
* 自定义插件
*/
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class CustomPlugin implements Interceptor {
/**
* 插件运行的代码,它将代替原有的方法
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("开始插入数据");
Object returnObject = invocation.proceed();
System.out.println("插入数据完成");
return returnObject;
}
/**
* 使用JDK的动态代理,给target对象创建一个delegate代理对象,以此来实现方法拦截和增强功能,它会回调intercept()方法。
* @param target
* @return
*/
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
/**
* 配置自定义相关属性
* @param properties
*/
@Override
public void setProperties(Properties properties) {
}
}
Invocation类:
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方法中写了什么
public static Object wrap(Object target, Interceptor interceptor) {
//1、获得自定义拦截器上的注解信息key为要拦截的接口的Class对象,value为该接口中的Method
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
//2、获得目标类中所有接口中被拦截的接口信息
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
//3、如果存在接口,则使用JDK动态代理,否则返回被代理对象
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
编写插件注解
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
@Intercepts()接口
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
Signature[] value();
}
1.getSignatureMap方法:
public class Plugin implements InvocationHandler {
//省略其他代码
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
//获取当前interceptor类的@Intercepts()注解反射数据
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
// issue #251
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
//获取value得值,也就是@Signature()注解的数据,可以配置多个@Signature()注解
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
for (Signature sig : sigs) {
//signatureMap中是否有@Signature()中type的值(我写的自定义注解中type是Executor.class)
//如果没有就添加一个key是type的值,value是HashSet,保证唯一性
Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
try {
//得到@Signature()中type的值(是一个类)反射的指定方法
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
}
signatureMap的数据格式如下:
key是注解中的type值的引用,value为method的值的方法。
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
2.接下来我们来看一下,获得目标类中所有接口中被拦截的接口信息这个方法
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
public class Plugin implements InvocationHandler {
//省略其他代码
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
Set<Class<?>> interfaces = new HashSet<>();
//只要type类不为空,就循环
while (type != null) {
//循环type类的所有接口
for (Class<?> c : type.getInterfaces()) {
//判断signatureMap中包不包含这个接口
if (signatureMap.containsKey(c)) {
//如果包含就添加到set集合中
interfaces.add(c);
}
}
//获得type类的父类
type = type.getSuperclass();
}
//将interfaces 集合转换成数组
return interfaces.toArray(new Class<?>[interfaces.size()]);
}
}
3.就是JDK动态代理了
public class Plugin implements InvocationHandler {
//省略其他代码
public static Object wrap(Object target, Interceptor interceptor) {
//如果存在接口,则使用JDK动态代理,否则返回被代理对象
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
/*这里实例化了Plugin类并且存放了代理类,
interceptor:责任链中的某一个具体实现类(我这里代表的是CustomPlugin类),
signatureMap:CustomPlugin类的@Intercepts注解解析的数据
*/
new Plugin(target, interceptor, signatureMap));
}
return target;
}
}
这里我们需要注意:
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;
}
//省略其他代码
}
是循环插件,每次都将target(被代理对象)传入到 interceptor.plugin 中,第一次生成一个代理对象,第二次循环的时候将第一次代理生成的对象作为参数,生成第二次代理对象,以次循环添加代理对象。动态代理就会生成如下图这样的结构:
返回的代理对象是$Proxy56@4947
(可以理解成pagehelper的代理类),h(Plugin @4943
)在生成的代理类是指向Plugin类,Plugin类的参数类型有:
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;
}
//省略其他代码
}
target:target是$Proxy56@4937
。
interceptor:interceptor指向的是PageInterceptor插件。
signatureMap:signatureMap指向的是PageInterceptor插件需要拦截的信息。
注意这个target会指向$Proxy56@4937
指向的是上一个代理类(可以理解成CustomPlugin的代理类,我们自己定义的插件)。
我们来看看$Proxy56@4937
这个代理类保存了什么数据:
h(Plugin @4930
)在生成的代理类是指向Plugin类。
target:target是CachintExecutor@4919
执行器。
interceptor:interceptor指向的是CustomPlugin插件。
signatureMap:signatureMap指向的是CustomPlugin插件需要拦截的信息也就是拦截update(修改)的sql。
我的插件在mybatis-config.xml配置文件中的顺序是:
<plugins>
<!--自定义的插件-->
<plugin interceptor="com.example.maybatissource.plugin.CustomPlugin"/>
<!--分页的插件-->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- config params as the following -->
<property name="param1" value="value1"/>
</plugin>
</plugins>
动态代理类执行顺序简图:
当有一个sql需要拦截的时候,我自定义的插件类拦截的是update(修改)语句,他会以次执行简图里的插件顺序从外到内:pagehelper->CustomPlugin。
为什么会先是pagehelper插件?
在mybatis-config.xml配置文件中pagehelper插件在最末尾也就是在interceptors集合中是最后一个,所以它先执行
public class InterceptorChain {
//省略其他代码
private final List<Interceptor> interceptors = new ArrayList<>();
}
在Plugin 类中的wrap方法中生成动态代理类的时候每次都会new Plugin(target, interceptor, signatureMap),这里的target第一次会是真正的被代理类,后面的几次都会是上一次jdk生成的代理类。接着就会调用有参构造方法为相对应的常量赋值。而pagehelper是最后一个插件,那么Plugin 类中的全局常量interceptor就是分页插件(pagehelper),signatureMap就是分页插件(pagehelper)@Intercepts()注解需要拦截的信息(可以是方法,参数,返回结果,Executor)。
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) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
我们看到当前分析的这个类Plugin 是实现了InvocationHandler 类的,所以我们找找看实现了invoke的方法
public class Plugin implements InvocationHandler {
//省略其他代码
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//method.getDeclaringClass()方法返回表示声明由此Method对象表示的方法的类的Class对象。
//简称当前方法(method)的所在的Class
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
//如果有需要拦截的方法
if (methods != null && methods.contains(method)) {
//1、拦截器的interceptor方法
return interceptor.intercept(new Invocation(target, method, args));
}
//2、直接调用方法
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
}
if (methods != null && methods.contains(method))
如果有需要拦截,就执行相对应的插件:interceptor.intercept(new Invocation(target, method, args));
如果没有,就执行return method.invoke(target, args);
调用下一个插件也就是我自定义的插件CustomPlugin,以次往下执行所有插件。层层过滤。