暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Mybatis插件机制

原创 我为啥没洁癖 2023-12-12
105

Mybatis插件机制

MyBatis 插件可以用来实现拦截器接口 Interceptor ,在实现类中对拦截对象和方法进行处理。

拦截器接口Interceptor

// Intercepts注解类的@Signature签名注解用于描述Invocation参数的信息 @Intercepts({ @Signature(type = Executor.class, method = "xxx", args = {xxx.class, xxx.class}) }) public interface Interceptor { // 执行拦截逻辑的方法,插件的核心方法 Object intercept(Invocation invocation) throws Throwable; //生成target的代理对象 //将当前的拦截器生成代理对象存到拦截器链中 default Object plugin(Object target) { return Plugin.wrap(target, this); } //传递插件所需参数 default void setProperties(Properties properties) { ... } }
复制

Invocation

Invocation是拦截逻辑方法的参数。

public class Invocation { private final Object target; // 拦截的对象信息,@Signature注解的type属性 private final Method method; // 拦截的方法信息,@Signature注解的method属性 private final Object[] args; // 拦截的对象方法中的参数,@Signature的args属性 public Invocation(Object target, Method method, Object[] args) { this.target = target; this.method = method; this.args = args; } // get... // 利用反射来执行拦截对象的方法 public Object proceed() throws InvocationTargetException, IllegalAccessException { return method.invoke(target, args); } }
复制

plugin方法

Mybatis在创建拦截器代理时候会判断一次,当前这个类 Interceptor 到底需不需要生成一个代理进行拦截,如果需要拦截,就生成一个代理对象,这个代理就是一个 {@link Plugin},它实现了jdk的动态代理接口 {@link InvocationHandler},如果不需要代理,则直接返回目标对象本身。

加载时机:该方法在 mybatis 加载核心配置文件时被调用

生成代理对象的方法

public class Plugin implements InvocationHandler { // 利用反射,获取这个拦截器 MyInterceptor 的注解 Intercepts和Signature,然后解析里面的值,    // 1 先是判断要拦截的对象是哪一个    // 2 然后根据方法名称和参数判断要对哪一个方法进行拦截    // 3 根据结果做出决定,是返回一个对象呢还是代理对象 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)); } return target; } }
复制

执行模式

当配置多个拦截器时, MyBatis 会遍历所有拦截器,按顺序执行拦截器的 plugin 口方法, 被拦截的对象就会被层层代理。

在执行拦截对象的方法时,会一层层地调用拦截器,拦截器通 invocation proceed()调用下层的方法,直到真正的方法被执行。

方法执行的结果 从最里面开始向外 层层返回,所以如果存在按顺序配置的三个签名相同的拦截器, MyBaits 会按照 C>B>A>target.proceed()>A>B>C 的顺序执行。如果签名不同, 就会按照 MyBatis 拦截对象的逻辑执行.

拦截器注解@Intercepts

拦截器接口上的注解@Intercepts决定了插件拦截的对象是什么,什么时候进行拦截。

@Intercepts({@Signature( type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}) }) @AllArgsConstructor public class MybatisEncryptInterceptor implements Interceptor { ... }
复制

@Intercepts({})注解中是一个@Signature()数组,可以在一个拦截器中同时拦截不同的接口和方法。

@Signature注解

包含以下三个属性:

  • type 设置拦截接口,可选值是前面提到的4个接口
  • method 设置拦截接口中的方法名 可选值是前面4个接口中所对应的方法,需要和接口匹配
  • args 设置拦截方法的参数类型数组 通过方法名和参数类型可以确定唯一一个方法

@Signature.type

指定拦截什么接口类,允许使用插件来拦截的接口包括以下几个:

  • Executor类:执行器Executor (update、query、commit、rollback等方法);
  • ParameterHandler:参数处理器ParameterHandler (getParameterObject、setParameters方法);
  • ResultSetHandler:结果集处理器ResultSetHandler(handleResultSets、handleOutputParameters等方法);
  • StatementHandler:SQL语法构建器StatementHandler (prepare、parameterize、batch、updates query等方 法);

@Signature.method

@Signature.method代表在@Signature.type配置的类基础上指定需要拦截的类中的方法

@Signature.args

@Signature.args是在@Signature.method的基础上指定方法的入参类型,按方法定义的顺序写。最终,通过类+方法+入参类型定位拦截位置。

配置方式

如下面示例代码配置了 type = ParameterHandler.class 、 method = “setParameters” 、args = {PreparedStatement.class}

代表拦截ParameterHandler类的setParameters方法且参数是PreparedStatement类型。

//示例注解配置 @Intercepts({@Signature( type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class}) })
复制

ParameterHandler类

public interface ParameterHandler { Object getParameterObject(); // 注解配置命中拦截的方法 void setParameters(PreparedStatement ps) throws SQLException; }
复制

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论