前言
最近在看mybatis plus的源码,发现org.apache.ibatis.executor这个包路径下的Executor在执行sql的时候会包装成一个责任链,其中这个Interceptor接口就是为了去扩展实现执行sql前后做一些自定义的处理,例如:打印sql信息,并把参数放入sql中进行打印;根据自定义注解加解密数据等。带着个人的兴趣,希望从应用及源码的角度为读者梳理MyBatis的责任链模式。
Interceptor(拦截器)接口
要定义自定义的拦截器的话,需要实现Interceptor接口,Mybatis plus给我们实现了一个MybatisPlusInterceptor类,具体的使用方式以及@Intercepts和@Signature的作用本文不做阐述,我们开始从源码的角度了解mybatis的责任链模式。
以下是Interceptor的源码:
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 invocation)、默认实现的plugin(Object target)方法,该方法返回的是一个对象、默认实现的一个空方法setProperties(Properties properties)返回值为void。
责任链模式是使多个对象都有机会处理请求,将这些对象连成一条链,这句话就是责任链模式定义的核心。
默认实现是的plugin方法就是将这些对象串起来的核心,所以我们查看Plugin.wrap(target, this)的具体实现,看下具体做了什么事情,为了文章的清晰,省略了后面一部分代码。
wrap方法的核心就是Proxy.newProxyInstance,创建代理对象。
Plugin类实现了JDK的动态代理接口InvocationHandler,所以当前这个类是代理类,代理的对象在调用方法时会进入invoke方法。
Plugin类中的target是目标对象,在mybaits中就是Executor;interceptor就是我们实现的拦截器;signatureMap一个签名map,具体内容和@Intercepts @Signature两个注解有关,有兴趣的可以单独了解。
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;
}
@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);
}
}
// 省略后续代码..........
InterceptorChain拦截器链
我们的自定义拦截器需要实现Interceptor接口,同时自定义的拦截器会被Plugin类进行代理,如何把代理对象形成链路,就是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);
}
}
我们根据代码画个图来总结一下链路的生成
从图中可以知道其实就是代理对象再次生成代理对象,特殊的是代理对象的target属性配合Invocation类中的proceed方法形成链路的调用,这样就可以在我们执行sql的前后,做一些特殊的自定的事情了。
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);
}
}
总结
本文通过mybatis的源码进行了责任链这个设计模式思想的一种学习,对于设计模式的学习,并不是很推荐从生活中的例子来进行学习,比如有的文章从古代三从四德的故事开始讲责任链模式,有点空洞。个人比较喜欢从代码的角度进行设计模式的学习。