mybatis之插件机制

插件

使用

  1. 使用插件意味着在修改mybatis的底层封装,虽然灵活但是也可能导致mybatis出现重大bug
  2. 在mybatis中使用插件就必须实现Interceptor接口

    public interface Interceptor {
    
      //直接覆盖所拦截对象原有的方法,通过Invocation反射调用原来对象的方法
      Object intercept(Invocation invocation) throws Throwable;
    // target指的是被拦截的对象,对拦截对象生成一个代理对象,并返回
      Object plugin(Object target);
    //在plugin元素中配置所需要的参数,在插件初始化的时候调用一次
      void setProperties(Properties properties);
    
    }
    
    public class InterceptorChain {
    
      private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
    
      public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
          target = interceptor.plugin(target);
        }
        return target;
      }
    
      public void addInterceptor(Interceptor interceptor) {
        interceptors.add(interceptor);
      }
    
    }
    
  3. mybatis提供了一个工具类Plugin,实现了InvocationHandler接口,采用jdk动态代理

    public class Plugin implements InvocationHandler {
    
      private Object target;
      private Interceptor interceptor;
      private Map<Class<?>, Set<Method>> signatureMap;
    
      private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {
        this.target = target;
        this.interceptor = interceptor;
        this.signatureMap = signatureMap;
      }
    
      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;
      }
    
      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)) {
            return interceptor.intercept(new Invocation(target, method, args));
          }
          return method.invoke(target, args);
        } catch (Exception e) {
          throw ExceptionUtil.unwrapThrowable(e);
        }
      }
    
      private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
        Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
        if (interceptsAnnotation == null) { // issue #251
          throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());      
        }
        Signature[] sigs = interceptsAnnotation.value();
        Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();
        for (Signature sig : sigs) {
          Set<Method> methods = signatureMap.get(sig.type());
          if (methods == null) {
            methods = new HashSet<Method>();
            signatureMap.put(sig.type(), methods);
          }
          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;
      }
    
      private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {
        Set<Class<?>> interfaces = new HashSet<Class<?>>();
        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()]);
      }
    
    }

确定需要拦截的签名

  1. Mybatis插件可以拦截Executor、StatementHandler、ParameterHandler、ResulteSetHandler中的任意一个。
    * Executor是执行SQL的全过程,包括组装参数,组装结果集返回和执行SQL过程
    * StatementHandler是执行SQL的过程,可以重写执行SQL的过程,这是常用的拦截对象
    * ParameterHandler,很明显它主要是拦截执行SQL的参数组装,你可以重写组装参数规则
    * ResultSetHandler用于拦截执行结果的组装,你可以重写组装结果的规则
  2. 案例

        配置插件,注意顺序
            * Mybatis3.X 的版本使用的 dtd 作为 XML 的格式校验文档。
            * 而在 XML 规范中,dtd 是有严格的顺序的,在报错的异常中已经列出了对应的顺序,
            * 应该为:(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
            objectWrapperFactory?,plugins?,environments?,databaseIdProvider?,mappers?)
    
        <plugins>
            <plugin interceptor="com.jannal.mybatis.plugin.MyPlugin">
                <property name="jannal" value="jannalpro" />
            </plugin>
        </plugins>
    
         /**
     * @Intercepts 标识它是一个拦截器
     * @Signature 注册拦截器签名的地方
     */
    @Intercepts(value = { @Signature(type = StatementHandler.class, // 确定要拦截的对象
            method = "prepare", // 确定要拦截的方法
            args = { Connection.class }// 拦截方法的参数
            ) })
    public class MyPlugin implements Interceptor {
        private Properties properties;
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            System.out.println("开始拦截.....");
            Object proceed = invocation.proceed();
            System.out.println("结束拦截.....");
            return proceed;
        }
    
        @Override
        public Object plugin(Object target) {
            System.out.println("生成代理对象....");
            return Plugin.wrap(target, this);
        }
    
        @Override
        public void setProperties(Properties properties) {
            System.out.println(properties.get("jannal"));
           this.properties = properties;
        }
    
    }
    
    生成代理对象....
    生成代理对象....
    生成代理对象....
    生成代理对象....
    2017-06-23 11:50:50.790 [main] DEBUG org.apache.ibatis.logging.slf4j.Slf4jImpl.debug(Slf4jImpl.java:43) - Openning JDBC Connection
    2017-06-23 11:50:50.998 [main] DEBUG org.apache.ibatis.logging.slf4j.Slf4jImpl.debug(Slf4jImpl.java:43) - Created connection 982757413.
    2017-06-23 11:50:51.001 [main] DEBUG org.apache.ibatis.logging.slf4j.Slf4jImpl.debug(Slf4jImpl.java:43) - ooo Using Connection [com.mysql.jdbc.JDBC4Connection@3a93b025]
    开始拦截.....
    2017-06-23 11:50:51.002 [main] DEBUG org.apache.ibatis.logging.slf4j.Slf4jImpl.debug(Slf4jImpl.java:43) - ==>  Preparing: select id ,mobile_no_internet ,cust_no ,mes_code ,mes_title ,mes_content ,create_time ,inserttime ,updatetime from t_user_message t where t.id=?; 
    结束拦截.....
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mybatis插件机是其非常重要和有用的功能之一,可以对 Mybatis 的执行过程进行拦截和修改,方便我们实现一些自定义的功能和需求。而 Mybatis插件机是通过 Interceptor 接口和 InterceptorChain 类实现的。 Interceptor 接口是 Mybatis 提供的拦截器接口,其定义了两个方法:intercept 和 plugin。其中 intercept 方法是用来拦截 Mybatis 的执行过程的,而 plugin 方法则是用来生成代理对象的。 InterceptorChain 类则是用来管理 Interceptor 实例的,是一个拦截器链。在 Mybatis 初始化时,会将所有的 Interceptor 实例添加到 InterceptorChain 中,当执行 SQL 时,会按顺序依次调用 InterceptorChain 中的 Interceptor 实例的 intercept 方法进行拦截。 下面我们来看一下如何实现一个自定义的 Interceptor。 首先,我们需要实现 Interceptor 接口,并实现其两个方法。 ```java public class MyInterceptor implements Interceptor{ @Override public Object intercept(Invocation invocation) throws Throwable { // 在这里编写拦截器逻辑 return invocation.proceed(); } @Override public Object plugin(Object target) { // 生成代理对象 return Plugin.wrap(target, this); } } ``` 在 intercept 方法中,我们可以编写一些自定义的拦截逻辑。在最后,一定要调用 invocation.proceed() 方法,否则 Mybatis 的执行过程将会被中止。 在 plugin 方法中,我们需要通过 Plugin.wrap(target, this) 方法生成一个代理对象。这个代理对象将会被添加到 InterceptorChain 中,用于拦截 Mybatis 的执行过程。 接下来,我们需要在 Mybatis 中配置我们的自定义 Interceptor。 ```xml <plugins> <plugin interceptor="com.example.MyInterceptor"/> </plugins> ``` 在配置文件中,我们需要添加一个 plugins 标签,并在其中添加一个 plugin 标签,指定我们的拦截器类的全限定名。 这样,我们就完成了一个简单的自定义 Interceptor 的编写和配置。 总结一下,Mybatis插件机是通过 Interceptor 接口和 InterceptorChain 类实现的。我们可以通过实现 Interceptor 接口来编写自定义的拦截器,并在配置文件中添加相应的配置来启用它。在实现 Interceptor 时,我们需要实现两个方法:intercept 和 plugin。其中 intercept 方法用于编写拦截逻辑,plugin 方法用于生成代理对象。在最后,一定要调用 invocation.proceed() 方法,否则 Mybatis 的执行过程将会被中止。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值