首先从代码包里面看到有一个org.apache.ibatis.plugins
的包,猜想就是这里可以进行扩展了,可以看到刚好有Interceptor
这个扩展点,那就先从Interceptor
入手。
无论实在JavaWeb当中的Filter,还是在Spring里面的HandlerInterceptor都可以或多或少的知道这个责任链模式,如果没有相关知识储备的,请自行查阅。
我们在这里可以看到有一个InterceptorChain
,这么一个东西,翻译过来就是拦截器链。
下面看一下是怎么个实现方式
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);
}
}
就是一个使用List存储拦截器的一个数据结构,这样看来这个只是一个存储结构,相应的执行逻辑并不在其中。
我们在寻址找到拦截器的定义。
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
}
}
可以看到这里面有两个方法
- intercept 这里是真正执行拦截的逻辑,里面封装一个Invocation的上下文,这里面包含了被拦截对象、被拦截方法、参数,就是一个java正常反射该有的东西。
- plugin 方法,这个就是创建target的代理。如果被包装对象是包含实现的接口的话,那么就使用jdk的动态代理方式创建一个代理实例。当然如果不是interface,那么就无能为例了,所以这里也预留了扩展,如果需要的话,可以自己扩展,当然这里主要是对
MapperInterface
进行拦截,所以大部分情况下,我们不需要自己扩展。
现在回过头来,再看上面的InterceptorChain#pluginAll
就是为当前的target创建一个代理链,在执行真正的方法之前或之后,会把链执行一遍。
那么找到对应的调用逻辑,就知道具体拦截的是什么了,但是从这里我们也可以知道,这个plugin的设计思路也可以用在我们的日常代码开发之中,比如时间统计、日志打印、性能分析等。
抛开以上题外话,继续了解我们的mybatis是怎么使用这个plugin的?具体是在哪里可以作为扩展点拦截呢?
我们经过溯源发现,这个拦截器链实在Configuration
里面进行配置的。
protected final InterceptorChain interceptorChain = new InterceptorChain();
可以看到有四个地方用到了。
- ParameterHandler 参数处理器
- ResultSetHandler 结果处理器
- StatementHandler 过程处理器
- Executor 执行器处理器
所以我们可以对这几个都可以进行拦截。
意味这样就完了吗?
不是的,由于这样的原因,有一点没有讲到,就是我们上面可以看到有四种类型的被拦截方法,肯定不会说是一个Interceptor对这个四种的每一种都生效,那么是如何实现的匹配呢?
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
Signature[] value();
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
Class<?> type();
String method();
Class<?>[] args();
}
有一个注解来声明具体的方法签名,可以看到type,method,args!!!
再看一下Plugin.wrap
的详细逻辑
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;
}
@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) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
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());
}
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
try {
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;
}
// 获取当前类的所有接口,
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
Set<Class<?>> interfaces = new HashSet<>();
while (type != null) {
for (Class<?> c : type.getInterfaces()) {
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class<?>[interfaces.size()]);
}
大概是这么一个过程
- 获取拦截器上面的注解
Interceptors
,然后拿到上面声明的被拦截方法签名,然后获取到被拦截的具体方法(找到配置的谁被拦截) - 继续获取到target上面的接口有多少是可以被拦截到的(找出实际是谁被拦截)
- 然后把被拦截的接口进行增强。(为实际的进行代理)
如下,是对ParameterHandler
的setParameters
方法进行增强。
@Intercepts({
@Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class})
})
public class ParameterHandlerInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object o = invocation.getTarget();
//System.out.println(JSONUtils.toJSONString(invocation));
System.out.println("==============ParameterHandlerInterceptor===================");
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
所以我们知道了,在Myabtis的拦截器实现里面,主要有几个元素
- 拦截器
- 拦截器链
- 拦截器定义
- 拦截器链工具类:以方法签名为条件的进行增强
这里和其它的拦截器稍微有些不一样的在于他是直接使用JDK代理实现了这个链的方式。
除此之外,还可以使用循环执行、链表的方式实现拦截器链。