-
插件调用过程:
MyBatis 插件使用的责任链模式, 这里的责任链模式是结合动态代理来实现的. 假设现在存在按顺序配置的 A, B, C 三个签名相同的拦截器, MyBatis 会按照 C>B>A>target.proceed()>A>B>C 顺序执行./** * 接口 */ public interface HelloWorld { void sayHelloWorld(); } /** * 接口实现 */ public class HelloWorldImpl implements HelloWorld { @Override public void sayHelloWorld() { System.out.println("Hello World") } } /** * 代理处理对象 */ public class InterceptorJdkProxy implements InvocationHandler { // 真实对象 private Object target; // 拦截器全限定名 private String interceptorClass; public IntegerceptorJdkProxy(Object target, String interceptorClass) { this.target = target; this.interceptorClass = interceptorClass; } public static Object bind(Object target, String interceptorClass) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InterceptorJdkProxy(target, interceptorClass)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (interceptorClass == null) { return method.invoke(target, args); } Object result = null; Interceptor interceptor = (Interceptor)Class.forName(interceptorClass).newInstance(); if (interceptor.before(proxy, target, method, args)) { result = method.invoke(target, args); } else { interceptor.around(proxy, target, method, args); } interceptor.after(proxy, target, method, args)l return result; } } /** * 拦截器接口 */ public interface Interceptor { public boolean before(Object proxy, Object target, Method method, Object[] args); public void around(Object proxy, Object target, Method method, Objects[] args); public void after(Object proxy, Object target, Method method, Objects[] args); } public class Interceptor1 implements Interceptor { public boolean before(Object proxy, Object target, Method method, Object[] args) { System.out.println("拦截器 1 的 before 方法"); return true; } public void around(Object proxy, Object target, Method method, Objects[] args) {} public void after(Object proxy, Object target, Method method, Objects[] args) { System.out.println("拦截器 1 的 after 方法"); } } public class Interceptor2 implements Interceptor { public boolean before(Object proxy, Object target, Method method, Object[] args) { System.out.println("拦截器 2 的 before 方法"); return true; } public void around(Object proxy, Object target, Method method, Objects[] args) {} public void after(Object proxy, Object target, Method method, Objects[] args) { System.out.println("拦截器 2 的 after 方法"); } } public class Interceptor3 implements Interceptor { public boolean before(Object proxy, Object target, Method method, Object[] args) { System.out.println("拦截器 3 的 before 方法"); return true; } public void around(Object proxy, Object target, Method method, Objects[] args) {} public void after(Object proxy, Object target, Method method, Objects[] args) { System.out.println("拦截器 3 的 after 方法"); } } public class Client { public static void main(String[] args) { // 这里的实际对象是 HelloImpl 实例 HelloWorld proxy1 = (HelloWorld)InterceptorJdkProxy.bind(new HelloImpl(), "study.Interceptor1"); // 注意这里实际对象是 proxy1 HelloWorld proxy2 = (HelloWorld)InterceptorJdkProxy.bind(proxy1, "study.Interceptor2"); // 注意这里实际对象是 proxy2 HelloWorld proxy3 = (HelloWorld)InterceptorJdkProxy.bind(proxy2, "study.Interceptor3"); proxy3.sayHelloWorld(); } }
调用
proxy3.sayHelloWorld()
方法会进入InterceptorJdkProxy#invoke()
方法, 这时产生一个 Interceptor3 实例对象, 然后调用该实例对象的before()
方法并返回 true, 然后调用 target 的sayHelloWorld()
方法, 这时的 target 为 proxy2;调用
proxy2.sayHelloWorld()
方法又会进入InterceptorJdkProxy#invoke()
方法, 这时产生一个 Interceptor2 实例对象, 然后调用该实例对象的before()
方法并返回 true, 然后调用 target 的sayHelloWorld()
方法, 这时的 target 为 proxy1;调用
proxy1.sayHelloWorld()
方法又会进入InterceptorJdkProxy#invoke()
方法, 这时产生一个 Interceptor1 实例对象, 然后调用该实例对象的before()
方法并返回 true, 然后调用 target 的sayHelloWorld()
方法, 这时的 target 为HelloWorldImpl
对象的实例.它的方法执行完毕后就会返回, 返回到
InterceptorJdkProxy#invoke()
方法中, 执行 Interceptor1 实例对象的 after 方法;Interceptor1 实例对象的 after 方法执行完毕后又返回到
InterceptorJdkProxy#invoke()
方法, 执行 Interceptor2 实例对象的 after 方法;
Interceptor2 实例对象的 after 方法执行完毕后又返回到InterceptorJdkProxy#invoke()
方法, 执行 Interceptor3 实例对象的 after 方法. 至此所有拦截器方法均被执行. 执行结果如下:拦截器 1 的 before 方法 拦截器 2 的 before 方法 拦截器 3 的 before 方法 Hello World 拦截器 3 的 after 方法 拦截器 2 的 after 方法 拦截器 1 的 after 方法
整个调用过程呈链式调用结构.
-
MyBatis 允许拦截的接口和方法
- Executor(update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler(getParameterObject, setParamters)
- ResultSetHandler(handleResultSets, handleCursorResultSets, handleOutputParameters)
- StatementHandler(prepare, parameterize, batch, update, query)
-
MyBatss 拦截器接口:
public interface Interceptor { Object intercept(Invocation invocation) throws Throwable; Object plugin(Object target); void setProperties(Properties properties); }
setProperties()
方法用来传递插件的参数, 该参数配置在 mybatis-config.xml 中:
<plugin interceptor="study.MyInterceptor"> <property name="prop1" value="value1"/> <property name="prop2" value="value2"/> </plugin>
plugin()
方法的 target 参数就是拦截器要拦截的对象, 该方法会在创建被拦截的接口实现类时被调用. 该方法的简单实现如下:
@Override public Object plugin(Object target) { return Plugin.wrap(target, this); }
intercept()
方法, 通过该方法的参数 invocation 可以得到很多有用的信息:
@Override public Object intercept(Invocation invocation) throws Throwable { Object target = invocation.getTarget(); Method method = invocation.getMethod(); Object[] args = invocation.getArgs(); Object result = invocation.proceed(); return result; }
-
拦截器签名:
- @Intercepts 注解, 它的属性是一个 @Signature 数组.
- @Signature 注解, 包含三个属性:
- type: 设置拦截的接口
- method: 设置拦截器接口中的方法名, 需要和接口匹配
- args: 设置拦截方法的参数类型数组, 通过方法名和参数类型可以确定唯一一个方法
-
下画线键值转小写驼峰形式插件:
@Intercepts( @Signatue( type=ResultSetHandler.class, method="handleResultSets", args={Statement.class} ) ) @SuppressWarnings({"unchecked", "rawtypes"}) public class CameHumpInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { // 这里拿到返回结果, 然后对返回结果进行处理 List<Object> list = (List<Object>)invocation.proceed(); for (Object object : list) { if (object instanceof Map) { processMap((Map)object); } else { break; } } return list; } // 省略其它方法 }
-
参考:
[1] : Java EE互联网轻量级框架整合开发
[2] : MyBatis从入门到精通
06-01
1317