MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
·Executor (update, query, flushStatements, commit,rollback, getTransaction, close, isClosed)
·ParameterHandler (getParameterObject, setParameters)
·ResultSetHandler (handleResultSets,handleOutputParameters)
·StatementHandler (prepare, parameterize, batch, update,query)
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。
下面先看一个插件的例子
@Intercepts({@Signature(type = StatementHandler.class,// 要拦截的类
method = "prepare",// 要拦截的方法
args = {Connection.class})// 方法的参数
})
public class MyPlugin implements Interceptor {
private String limitNum;
public Object intercept(Invocation invocation) throws Throwable {
// 此处可以写处理代码
System.out.println("MyPlugin");
return invocation.proceed();
}
public Object plugin(Object o) {
// 生成代理对象
return Plugin.wrap(o,this);
}
public void setProperties(Properties properties) {
// 可以获取配置文件中配置的属性
this.limitNum = properties.getProperty("limitNum", "30");
}
}
在配置文件中配置插件
这样一个插件就写完了。这个插件将会拦截在StatementHandler
实例中所有的 “prepare” 方法调用
我们简单看下插件的实现。
首先在解析
mybatis
的配置文件的时候,
会对
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
Iterator i$ = parent.getChildren().iterator();
while(i$.hasNext()) {
XNode child = (XNode)i$.next();
// 获取配置的插件的interceptor属性
String interceptor = child.getStringAttribute("interceptor");
// 解析节点下配置的
Properties properties = child.getChildrenAsProperties();
// 产生插件的实例
Interceptor interceptorInstance = (Interceptor)this.resolveClass(interceptor).newInstance();
// 设置property
interceptorInstance.setProperties(properties);
// 保存插件实例到插件链中
this.configuration.addInterceptor(interceptorInstance);
}
}
}
我们知道在产生
StatementHandler
实例的时候,
代码实现是通过调用
Configuration
的
newStatementHandler
方法来实现的
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 产生实例
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 产生代理对象
StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
我们直接看代理对象的产生,此方法会循环产生代理对象,多少个插件就会生成多少个代理对象,然后会形成一个代理链。
public Object pluginAll(Object target) {
Interceptor interceptor;
for(Iterator i$ = this.interceptors.iterator(); i$.hasNext(); target = interceptor.plugin(target)) {
interceptor = (Interceptor)i$.next();
}
return target;
}
继续查看Interceptor的plugin方法,我们看到interceptor实际上是我们自己定义的插件。我们的插件一般都是直接用Plugin的wrap方法来生成代理对象。
public Object plugin(Object o) {
// 生成代理对象
return Plugin.wrap(o,this);
}
我们进入这个方法
public static Object wrap(Object target, Interceptor interceptor) {
Map, Set> signatureMap = getSignatureMap(interceptor);
Class> type = target.getClass();
Class>[] interfaces = getAllInterfaces(type, signatureMap);
return interfaces.length > 0 ? Proxy.newProxyInstance(type.getClassLoader(), interfaces, new Plugin(target, interceptor, signatureMap)) : target;
}
我们看
Plugin
的
getSignatureMap
方法
private static Map, Set> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = (Intercepts)interceptor.getClass().getAnnotation(Intercepts.class);
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
} else {
Signature[] sigs = interceptsAnnotation.value();
Map, Set> signatureMap = new HashMap();
Signature[] arr$ = sigs;
int len$ = sigs.length;
for(int i$ = 0; i$ < len$; ++i$) {
Signature sig = arr$[i$];
Set methods = (Set)signatureMap.get(sig.type());
if (methods == null) {
methods = new HashSet();
signatureMap.put(sig.type(), methods);
}
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
((Set)methods).add(method);
} catch (NoSuchMethodException var10) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + var10, var10);
}
}
return signatureMap;
}
}
继续看
getAllInterfaces
方法(获取要代理的接口)
private static Class>[] getAllInterfaces(Class> type, Map, Set> signatureMap) {
HashSet interfaces;
for(interfaces = new HashSet(); type != null; type = type.getSuperclass()) {
Class[] arr$ = type.getInterfaces();
int len$ = arr$.length;
for(int i$ = 0; i$ < len$; ++i$) {
Class> c = arr$[i$];
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
}
return (Class[])interfaces.toArray(new Class[interfaces.size()]);
}
如果有要代理的接口就会产生代理对象,后面在用此对象调用方法的时候都会调用
Plugin
的
invoke
方法。我们看一下此方法的实现
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set methods = (Set)this.signatureMap.get(method.getDeclaringClass());
return methods != null && methods.contains(method) ? this.interceptor.intercept(new Invocation(this.target, method, args)) : method.invoke(this.target, args);
} catch (Exception var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
然后判断要执行的方法是否要被插件拦截,如果不要就直接回调原来的方法,否则就直接调用自定义插件的intercept方法
public Object intercept(Invocation invocation) throws Throwable {
// 此处可以写处理代码
System.out.println("MyPlugin");
return invocation.proceed();
}
在此方法中可以做我们自定义的事情,然后再回调原来的方法就可以了。