插件调用过程:
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 允许拦截的接口和方法
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(
@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从入门到精通