Mybatis插件源码阅读

作者 : 开心源码 本文共3153个字,预计阅读时间需要8分钟 发布时间: 2022-05-12 共162人阅读

mybatis插件需要实现 Interceptor 接口:

public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
  • intercept方法使用于实现阻拦
  • plugin 使用于将对象编程代理商对象,之后执行调使用时通过代理商调使用 intercept方法
  • setProperties方法作使用是为自己设置插件增加自己设置属性

1.intercept

2.plugin

public class Plugin implements InvocationHandler {

从上面代码可以看出Plugin类是一个动态代理商类。接下来我们来看一下是如何生成代理商对象的。

public Object plugin(Object target) {
return Plugin.wrap(target, this);
}

大多数情况下,用mybatis提供的Plugin.warp方法来生成代理商对象,当然也可以自己写。下面我们来看Plgin.wrap具体实现:

public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set> 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;
}
  • getSignatureMap(interceptor)方法获取阻拦的签名
private static Map<Class<?>, Set> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation =
interceptor.getClass().getAnnotation(Intercepts.class);
// issue #251
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation " +
interceptor.getClass().getName());
}
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set> signatureMap = new HashMap();
for (Signature sig : sigs) {
Set methods = signatureMap.computeIfAbsent(sig.type(),
k -> new HashSet());
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on "
+ sig.type() + " named " + sig.method()
+ ". Cause: " + e, e);
}
}
return signatureMap;
}
  • 判断插件实现类上面有没有 Intercepts 注解
  • 获取注解的值,值是Signature数组
  • 通过 Signature 指定的 type、method、args参数来获取 type指定类型的方法Method,并放入到签名map中。如下相似
@Intercepts(
{
@Signature(type = Executor.class, method = "query",
args = {
MappedStatement.class,
Object.class,
RowBounds.class,
ResultHandler.class}),
@Signature(type = Executor.class, method = "query",
args = {
MappedStatement.class,
Object.class,
RowBounds.class,
ResultHandler.class,
CacheKey.class,
BoundSql.class}),
}
)

其中type有4种:

  • Executor:使用于执行sql的全过程,包括组装参数、组装结果集返回和执行SQL过程都可以被阻拦;
  • StatementHandler:执行SQL的过程
  • ParameterHandler:阻拦SQL执行参数组装过程,可以重写组装参数规则
  • ResultSetHandler:阻拦执行结果集的组装,可以重写组装结果的规则

method:就是指定阻拦type的哪个方法

args:指定阻拦method需要的参数。

  • getAllInterfaces方法主要作使用的查看该类有无实现阻拦器注解中申明要阻拦的签名type:
private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, 
Set> signatureMap) {
Set interfaces = new HashSet();
while (type != null) {
for (Class<?> c : type.getInterfaces()) {
if (signatureMap.containsKey(c)) {
interfaces.add(c);
}
}
type = type.getSuperclass();
}
return interfaces.toArray(new Class<?>[interfaces.size()]);
}
  • 假如阻拦的对象实现了阻拦器注解公告的type接口,那么将创立一个代理商对象,代理商对象的InvocationHandler就是Plugin类,这样在实际调使用时将由Plugin的invoke方法进行解决。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set methods = signatureMap.get(method.getDeclaringClass());
if (methods != null &amp;&amp; methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
  • 判断当前调使用的方法能否在要阻拦的方法前面Map中;
  • 在阻拦签名Map中,直接调使用Interceptor的intercept方法,也就是插件实现类中的intercept方法;
  • 不在阻拦签名Map中,直接调使用真实代码执行就可。

下面是我的微信公众号 “小兵JAVA学习记录”,谢谢关注。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Mybatis插件源码阅读

发表回复