Mybatis 拦截器 源码分析
一.拦截器简介
本节不介绍使用,先说原理
Mybatis 定义了四个处理器,用于做 SQL 执行的默认处理;如果我们不添加拦截器,则 Mybatis 会按默认操作进行处理。
如果添加了拦截器,则其会先执行拦截器内的增强处理,基于JDK动态代理实现,再进行默认操作
PageHelper 分页插件就是基于拦截器实现的
其次,自定义拦截器要注意顺序问题,先注册的后生效,后注册的先生效,为什么这样下面会提到
二.拦截器实现说明
参考 Mybatis 实现写了一个演示 Demo,Mybatis 的应用下节介绍
1.拦截器链
/**
* 拦截器链
*/
static class InterceptorChain{
private final List<Interceptor> interceptors = new ArrayList<>();
/**
* 获取SqlSession时生成拦截器链代理类
* @param target
* @return
*/
public Object pluginAll(Object target) {
//第一次添加的拦截器对象,生成代理对象,原对象指向新生成的代理对象
//第二次新的代理对象作为原对象,生成代理对象...
//循环,直到所有注册的拦截器都遍历一遍
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
/**
* 依次添加拦截器实现
* @param interceptor
*/
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
}
2.拦截器接口
用于实现自定义拦截器的接口,可以在实现类内添加自定义的操作
/**
* 拦截器接口
* 拦截方法参数类,封装了 被代理类、方法对象类、参数 属性
*/
public interface Interceptor {
/**
* 拦截方法
* @param invocation
* @return
* @throws Throwable
*/
Object intercept(Invocation invocation) throws Throwable;
/**
* 插件包裹:将目标类封装逐层包裹
* @param target
* @return
*/
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}
3.执行器类
封装了被代理对象、方法对象、参数集合三个属性,还有一个 proceed 方法,proceed 内基于反射实现了被代理类方法的调用;
此类即为拦截器方法的参数
/**
* 执行器
*/
static 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;
}
/**
* Proceed 实际就是反射中方法对象的Invoke
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}
4.拦截器注解定义
1.拦截器注解
用于标识,哪个类是 Mybatis 拦截类,同时如果在SpringBoot框架下可结合 @Component 注解直接将自定义拦截器注册到上面的链内,但是要注意
顺序
问题,多个自定义拦截器最好手动注册,当前类取了下面的签名类集合作为值
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
/**
* 签名注解
* @return
*/
Signature[] value();
}
2.方法签名注解
签名注解,表明拦截的是哪个处理器的哪个方法,即三个属性:方法名、类、方法参数类型;
此处取值原理仍与反射机制相关,不清楚反射的可以参考下面Demo理解一下
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature{
/**
* 方法名,拦截类,方法参数类型
* @return
*/
String method();
Class<?> type();
Class<?>[] args();
}
3.反射Demo
package entity;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @author
* @date 2022-10-16 21:37
* @since 1.8
*/
public class ReflexTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Reflex reflex = new Reflex();
Method method = Reflex.class.getMethod("print", Integer.class);
method.invoke(reflex,0);
method = Reflex.class.getMethod("print", Integer.class,String.class);
method.invoke(reflex,1,"model");
}
static class Reflex{
/**
* 打印测试
* @param type
*/
public void print(Integer type){
System.out.println("单一参数:" + type);
}
/**
* 打印测试
* @param type
* @param model
*/
public void print(Integer type,String model){
System.out.println("多个参数:" + type + ":" + model);
}
}
}
5.默认处理器类
定义了两个默认处理器接口,用于演示,
Mybatis 实际存在四个处理器类,用于实现参数处理、SQL处理、结果处理等操作
;自定义拦截器是实现拦截器接口,不是实现处理器接口,处理器接口用于注册方法签名时要处理哪个处理器类的哪个方法
1.处理器一
接口
/**
* 处理器接口
*/
public interface FirstHandler{
String query(String type,Integer param);
}
实现
//默认实现
static class FirstHandlerImpl implements FirstHandler{
@Override
public String query(String type,Integer param) {
switch (param){
case 0:
return "零";
case 1:
return "一";
case 2:
return "二";
default:
return "default";
}
}
}
2.处理器二
接口
/**
* 处理器接口
*/
public interface SecondHandler{
String select(Integer param);
}
实现
//默认实现
static class SecondHandlerImpl implements SecondHandler{
@Override
public String select(Integer param) {
switch (param){
case 0:
return "SecondHandler:零";
case 1:
return "SecondHandler:一";
case 2:
return "SecondHandler:二";
default:
return "SecondHandler:default";
}
}
}
6.插件类(核心操作)
此类定义了一个 wrap 方法,用于包裹被代理类,会从默认处理器开始按注册顺序逐层包裹,最后全部包裹完成,返回代理对象,通过代理对象调用时会从外向内逐层调用,Plugin 完全取自Mybatis,没有修改,同学可以根据注释自己理解一下,
这也是上面提到的先注册后执行,后注册先执行的原理,同时可能因此产生覆盖效果(后处理的覆盖先处理的)
/**
* 插件类生成动态代理,实现InvocationHandler
*/
static 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;
}
/**
* 包裹拦截器
* @param target
* @param interceptor
* @return
*/
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) {
//基于JDK动态代理方式(接口反射)生成代理类
return Proxy.newProxyInstance(
type.getClassLoader(),
//接口类
interfaces,
//封装了每层循环的原始被代理对象,拦截器类 和 注册的实现方法
new Plugin(target, interceptor, signatureMap));
}
//否则返回被代理对象(原对象)
return target;
}
/**
* 调用(增强实现)
*
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> 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) {
//TODO 抛出异常
System.out.println(e);
return null;
}
}
/**
* 根据签名注解取出要处理的类和方法
* @param interceptor
* @return
*/
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
//获取自定义拦截器上的拦截器类注解
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
//TODO 非空校验,如果没有指定的注解,则无需向下解析了
//获取值,签名集合
Signature[] sigs = interceptsAnnotation.value();
//定义一个 HashMap 缓存签名类和方法集合
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
for (Signature sig : sigs) {
//按操作类取方法集合,存在则取出,否则新建
Set<Method> methods = computeIfAbsent(signatureMap, sig.type(), k -> new HashSet<>());
try {
//根据方法名和参数类型,取签名类的方法对象,并添加到集合
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
//TODO 抛出异常
}
}
//添加完成后返回签名缓存
return signatureMap;
}
/**
* 递归取出所有实现的接口类集合
* @param type
* @param signatureMap
* @return
*/
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
//定义接口类集合
Set<Class<?>> interfaces = new HashSet<>();
//类不为NULL
while (type != null) {
//遍历目标类所实现的接口类
for (Class<?> c : type.getInterfaces()) {
//如果是被注册的方法类,就添加到集合
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
//取父类(单继承多实现,如果没有显示继承某个父类,则取出结果为Object类,再取则为NULL,循环结束)
type = type.getSuperclass();
}
//返回注册的接口类的数组
return interfaces.toArray(new Class<?>[0]);
}
/**
* 对 MAP 官方方法做了一层封装,不存在则创建一个值对象并返回
* @param map
* @param key
* @param mappingFunction
* @return
* @param <K>
* @param <V>
*/
public static <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<K, V> mappingFunction) {
V value = map.get(key);
if (value != null) {
return value;
}
return map.computeIfAbsent(key, mappingFunction);
}
}
7.自定义拦截器(测试)
上面的代码已经实现了拦截器框架,下面定义两个测试类验证一下
1.混合拦截
@Intercepts({
@Signature(method = "query", type = FirstHandler.class,args = {String.class,Integer.class}),
@Signature(method = "query", type = SecondHandler.class,args = {Integer.class}),
})
static class MyInterceptor implements Interceptor{
/**
* 拦截器实现
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
//TODO 增强处理
invocation.getArgs()[1] = 2;
//反射对象Invocation,Proceed实际封装了方法调用:method.invoke(target, args) 此处即被代理类调用
return invocation.proceed();
}
}
2.仅拦截第一类处理器
/**
* 第二处理 拦截 1
*/
@Intercepts({
@Signature(method = "select", type = SecondHandler.class,args = {Integer.class}),
})
static class SecondInterceptorOne implements Interceptor{
/**
* 拦截器实现
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
//TODO 增强处理
invocation.getArgs()[0] = 1;
//反射对象Invocation,Proceed实际封装了方法调用:method.invoke(target, args) 此处即被代理类调用
return invocation.proceed();
}
}
3.仅拦截第二类处理
/**
* 第二处理 拦截 2
*/
@Intercepts({
@Signature(method = "select", type = SecondHandler.class,args = {Integer.class}),
})
static class SecondInterceptorTwo implements Interceptor{
/**
* 拦截器实现
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
//TODO 增强处理
invocation.getArgs()[0] = 2;
//反射对象Invocation,Proceed实际封装了方法调用:method.invoke(target, args) 此处即被代理类调用
return invocation.proceed();
}
}
三.测试
将上面所有类和方法写到一个测试类,并通过主方法调用验证
package entity;
import java.lang.annotation.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;
import java.util.function.Function;
/**
* @author
* @date 2022-10-12 17:43
* @since 1.8
*/
public class TEST {
/**
* 测试类
* 拦截器链是先注册拦截器实现类,再逐层生成动态代理,
* 最先注册的是最原始被代理对象,最后注册的是最新代理对象,
* 所以使用时是最后注册的拦截器类先生效,先注册的后生效,同时可能因此后生效的会覆盖先生效的处理
*
* Mybatis 拦截器在使用时,他会自己最先 New 一个默认的被代理类作为参数传给 pluginAll 然后才开始包裹拦截器链的实现类
* @param args
*/
public static void main(String[] args) {
InterceptorChain chain = new InterceptorChain();
chain.addInterceptor(new MyInterceptor());
chain.addInterceptor(new SecondInterceptorOne());
chain.addInterceptor(new SecondInterceptorTwo());
FirstHandler firsthandler = (FirstHandler) chain.pluginAll(new FirstHandlerImpl());
String result = firsthandler.query("",0);
System.out.println(result);
SecondHandler secondHandler = (SecondHandler) chain.pluginAll(new SecondHandlerImpl());
System.out.println(secondHandler.select(0));
}
@Intercepts({
@Signature(method = "query", type = FirstHandler.class,args = {String.class,Integer.class}),
@Signature(method = "query", type = SecondHandler.class,args = {Integer.class}),
})
static class MyInterceptor implements Interceptor{
/**
* 拦截器实现
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
//TODO 增强处理
invocation.getArgs()[1] = 2;
//反射对象Invocation,Proceed实际封装了方法调用:method.invoke(target, args) 此处即被代理类调用
return invocation.proceed();
}
}
/**
* 第二处理 拦截 1
*/
@Intercepts({
@Signature(method = "select", type = SecondHandler.class,args = {Integer.class}),
})
static class SecondInterceptorOne implements Interceptor{
/**
* 拦截器实现
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
//TODO 增强处理
invocation.getArgs()[0] = 1;
//反射对象Invocation,Proceed实际封装了方法调用:method.invoke(target, args) 此处即被代理类调用
return invocation.proceed();
}
}
/**
* 第二处理 拦截 2
*/
@Intercepts({
@Signature(method = "select", type = SecondHandler.class,args = {Integer.class}),
})
static class SecondInterceptorTwo implements Interceptor{
/**
* 拦截器实现
* @param invocation
* @return
* @throws Throwable
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
//TODO 增强处理
invocation.getArgs()[0] = 2;
//反射对象Invocation,Proceed实际封装了方法调用:method.invoke(target, args) 此处即被代理类调用
return invocation.proceed();
}
}
/**
* 拦截器链
*/
static class InterceptorChain{
private final List<Interceptor> interceptors = new ArrayList<>();
/**
* 获取SqlSession时生成拦截器链代理类
* @param target
* @return
*/
public Object pluginAll(Object target) {
//第一次添加的拦截器对象,生成代理对象,原对象指向新生成的代理对象
//第二次新的代理对象作为原对象,生成代理对象...
//循环,直到所有注册的拦截器都遍历一遍
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
/**
* 依次添加拦截器实现
* @param interceptor
*/
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
}
/**
* 拦截器接口
* 拦截方法参数类,封装了 被代理类、方法对象类、参数 属性
*/
public interface Interceptor {
/**
* 拦截方法
* @param invocation
* @return
* @throws Throwable
*/
Object intercept(Invocation invocation) throws Throwable;
/**
* 插件包裹:将目标类封装逐层包裹
* @param target
* @return
*/
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
}
/**
* 处理器接口
*/
public interface FirstHandler{
String query(String type,Integer param);
}
//默认实现
static class FirstHandlerImpl implements FirstHandler{
@Override
public String query(String type,Integer param) {
switch (param){
case 0:
return "零";
case 1:
return "一";
case 2:
return "二";
default:
return "default";
}
}
}
/**
* 处理器接口
*/
public interface SecondHandler{
String select(Integer param);
}
//默认实现
static class SecondHandlerImpl implements SecondHandler{
@Override
public String select(Integer param) {
switch (param){
case 0:
return "SecondHandler:零";
case 1:
return "SecondHandler:一";
case 2:
return "SecondHandler:二";
default:
return "SecondHandler:default";
}
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
/**
* 签名注解
* @return
*/
Signature[] value();
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature{
/**
* 方法名,拦截类,方法参数类型
* @return
*/
String method();
Class<?> type();
Class<?>[] args();
}
/**
* 执行器
*/
static 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;
}
/**
* Proceed 实际就是反射中方法对象的Invoke
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public Object proceed() throws InvocationTargetException, IllegalAccessException {
return method.invoke(target, args);
}
}
/**
* 插件类生成动态代理,实现InvocationHandler
*/
static 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;
}
/**
* 包裹拦截器
* @param target
* @param interceptor
* @return
*/
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) {
//基于JDK动态代理方式(接口反射)生成代理类
return Proxy.newProxyInstance(
type.getClassLoader(),
//接口类
interfaces,
//封装了每层循环的原始被代理对象,拦截器类 和 注册的实现方法
new Plugin(target, interceptor, signatureMap));
}
//否则返回被代理对象(原对象)
return target;
}
/**
* 调用(增强实现)
*
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> 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) {
//TODO 抛出异常
System.out.println(e);
return null;
}
}
/**
* 根据签名注解取出要处理的类和方法
* @param interceptor
* @return
*/
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
//获取自定义拦截器上的拦截器类注解
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
//TODO 非空校验,如果没有指定的注解,则无需向下解析了
//获取值,签名集合
Signature[] sigs = interceptsAnnotation.value();
//定义一个 HashMap 缓存签名类和方法集合
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
for (Signature sig : sigs) {
//按操作类取方法集合,存在则取出,否则新建
Set<Method> methods = computeIfAbsent(signatureMap, sig.type(), k -> new HashSet<>());
try {
//根据方法名和参数类型,取签名类的方法对象,并添加到集合
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
//TODO 抛出异常
}
}
//添加完成后返回签名缓存
return signatureMap;
}
/**
* 递归取出所有实现的接口类集合
* @param type
* @param signatureMap
* @return
*/
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
//定义接口类集合
Set<Class<?>> interfaces = new HashSet<>();
//类不为NULL
while (type != null) {
//遍历目标类所实现的接口类
for (Class<?> c : type.getInterfaces()) {
//如果是被注册的方法类,就添加到集合
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
//取父类(单继承多实现,如果没有显示继承某个父类,则取出结果为Object类,再取则为NULL,循环结束)
type = type.getSuperclass();
}
//返回注册的接口类的数组
return interfaces.toArray(new Class<?>[0]);
}
/**
* 对 MAP 官方方法做了一层封装,不存在则创建一个值对象并返回
* @param map
* @param key
* @param mappingFunction
* @return
* @param <K>
* @param <V>
*/
public static <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<K, V> mappingFunction) {
V value = map.get(key);
if (value != null) {
return value;
}
return map.computeIfAbsent(key, mappingFunction);
}
}
}
结果
类 | 拦截 | 操作 |
---|---|---|
MyInterceptor | FirstHandler 的 query(String type,Integer param) | 将第 2 个参数改为 2 |
MyInterceptor | SecondHandler 的 query(Integer param) | 方法不存在,所以不生效 |
SecondInterceptorOne | SecondHandler 的 select(Integer param) | 将参数改为 1 |
SecondInterceptorTwo | SecondHandler 的 select(Integer param) | 将参数改为 2 |
拦截到处理器的调用,并修改其参数
最里面包裹的是默认实现
整体输出效果(部分异常捕获未处理,Mybatis 实际会抛出),SecondHandler 原始为 0 ,先修改为 2 ,后又修改为 1 ,修改被覆盖,最终返回了 “一”