查看MyBatis官方文档发现,MyBatis允许插件拦截调用的方法包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
mybatis中有一个plugin包,包下面有上面几个类。在这个包里面,自定义了一个异常类PluginException继承的RuntimeException运行时异常。Plugin类implements InvocationHandler。
InterceptorChain这个类中有个属性List interceptors存放的是配置文件中每一个对应的plugin。而InterceptorChain中的pluginAll(Object target)就是实现AOP扩展功能的方法媒介。只要调用了这个方法返回的对象,就说名这个类是可以被拦截的。通过搜索方法调用发现刚好和文档的四个类对应上。
//InterceptorChain,实现AOP扩展功能
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
//Interceptor接口默认的方法
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
//Plugin
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));//:Plugin实现InvocationHandler接口
}
return target;
}
//代理对象
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)) {
//注释的方法不为空,而且包含类调用的方法
//看拦截器怎么具体实现的intercept方法
return interceptor.intercept(new Invocation(target, method, args));
}
//没有拦截器就正常调用
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
读取配置文件,将plugin的类插入InterceptorChain的属性list集合中
//XMLConfigBuilder解析pluginsxml文档的代码
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
//resolveClass(interceptor)这个没有配置别名在注册中心,通过全名进行类加载获得class,通过反射创建对象
Interceptor interceptorInstance = (Interceptor)resolveClass(interceptor).getDeclaredConstructor().newInstance();
//plugin节点下的key、value键值对转换成Properties对象赋值给Interceptor对象
interceptorInstance.setProperties(properties);
//configuration对象中的InterceptorChain对象添加Interceptor拦截器对象
configuration.addInterceptor(interceptorInstance);
}
}
}
> plugin简单实现example
~~~java
//插件实现Interceptor接口
@Intercepts(@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class}))
public class MyPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//方法调用之前处理
return invocation.proceed();
}
}
<!--mybatis-config.xml-->
<plugins>
<plugin interceptor="com.study.plugin.MyPlugin">
<property name="" value=""/>
</plugin>
</plugins>
//最终一个简单查询语句会执行到executor.query,要想实现拦截,executor对象的创建必须要实现动态代理扩展,AOP
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}