> 文章列表 > 聊聊Mybatis中的责任链模式

聊聊Mybatis中的责任链模式

聊聊Mybatis中的责任链模式

前言

最近在看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;}@Overridepublic 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的源码进行了责任链这个设计模式思想的一种学习,对于设计模式的学习,并不是很推荐从生活中的例子来进行学习,比如有的文章从古代三从四德的故事开始讲责任链模式,有点空洞。个人比较喜欢从代码的角度进行设计模式的学习。

中文学习