plugin的实现和原理(mybatis3.4.6)

Mybatis

plugin原理和实现思路

摘要:本文主要讲解3.6.4版本mybatis下plugin的原理和实现思路;

包目录说明

该包整体类比较少,但实现插件的思路指的学习和模仿,包下主要有Interceptor(拦截器顶级接口)、InterceptorChain(责任链)、Intercepts和Signature(两个注解配合,标定那些类和方法需要拦截)、Invocation(反射机制类)、Plugin(插件对象本身)、PluginException(异常类);

plugin的实现和原理(mybatis3.4.6)_第1张图片

包的层级图

在开始说如何实现插件之前,需要了解一下,mybatis的执行概要图和mybatis的核心类:

plugin的实现和原理(mybatis3.4.6)_第2张图片

执行概要图

核心类:

 从MyBatis代码实现的角度来看,MyBatis的主要的核心部件有以下几个:


Configuration初始化基础配置,比如MyBatis的别名等,一些重要的类型对象,如,插件,映射器,ObjectFactory和typeHandler对象,MyBatis所有的配置信息都维持在Configuration对象之中

SqlSessionFactory 

SqlSession工厂

SqlSession作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能

Executor

MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护

StatementHandler   封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。

ParameterHandler   负责对用户传递的参数转换成JDBC Statement 所需要的参数,

ResultSetHandler    负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;

TypeHandler          负责java数据类型和jdbc数据类型之间的映射和转换

MappedStatement   MappedStatement维护了一条节点的封装,

SqlSource            负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回

BoundSql表示动态生成的SQL语句以及相应的参数信息


类的说明:

Interceptor

拦截器接口,所有的拦截器都需要实现;代码如下

public interface Interceptor {


  //拦截的方法(里面需要执行invocation的proceed方法,用于执行代码对象的自身方法)

 Object intercept(Invocation invocation) throws Throwable;


 //获取代理对象

 Object plugin(Object target);


  //暂时没有看到一些用处,可以自己注入一下属性

 void setProperties(Properties properties);


}


Invocation

执行类(反射类),代码如下

public class Invocation {


  //代码对象

 private final Object target;

// 代码方法

 private final Method method;

  //参数

 private final Object[] args;

  //这些属性,刚好可以执行反射机制中的方法


 public Invocation(Object target, Method method, Object[] args) {

   this.target = target;

   this.method = method;

   this.args = args;

  }


 public Object getTarget() {

   return target;

  }


 public Method getMethod() {

   return method;

  }


 public Object[] getArgs() {

   return args;

  }


  //每次拦截之后,需要调用这个方法,用于执行本身的方法

 public Object proceed() throws InvocationTargetException,IllegalAccessException {

   return method.invoke(target, args);

  }


}


Intercepts和Signature

两个注解配合使用,Intercepts标记类,Signature标记拦击的类、方法、和参数;

public @interface Signature {

 Class type();


 String method();


 Class[] args();

}

Plugin

publicclass Plugin implements InvocationHandler {


  //代理对象

  private final Object target;

// 拦截对象

  private final Interceptor interceptor;

// 存放的是类和方法(通过注解获取到的)

  private final Map,Set> signatureMap;


  private Plugin(Object target, Interceptorinterceptor, Map, Set> signatureMap) {

    this.target = target;

    this.interceptor = interceptor;

    this.signatureMap = signatureMap;

  }


  //封装插件对象

  public static Object wrap(Object target,Interceptor interceptor) {

    //可以看一下源码了,就是简单的反射机制代码了

    Map,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;

  }


  //执行方法,如果方法存在map中,就执行拦截(interceptor的intercept方法)

  @Override

  public Object invoke(Object proxy, Methodmethod, Object[] args) throws Throwable {

    try {

      Set methods =signatureMap.get(method.getDeclaringClass());

      if (methods != null &&methods.contains(method)) {

        return interceptor.intercept(newInvocation(target, method, args));

      }

      return method.invoke(target, args);

    } catch (Exception e) {

      throw ExceptionUtil.unwrapThrowable(e);

    }

  }

InterceptorChain

责任链,初始化的时候,赋值了所有的拦截器,代码也很简单,如下

public class InterceptorChain {


 private final List interceptors = newArrayList();


 public Object pluginAll(Object target) {

   for (Interceptor interceptor : interceptors) {

     target = interceptor.plugin(target);

    }

   return target;

  }


 public void addInterceptor(Interceptor interceptor) {

   interceptors.add(interceptor);

  }


 public List getInterceptors() {

   return Collections.unmodifiableList(interceptors);

  }

}

值得注意的是,什么时候interceptors赋的值?这里就关联到了Configuration这个类,目前有两种初始化的方式:

[if !supportLists]1、  [endif]通过SqlSessionFactoryBean去构建Configuration添加拦截器并构建获取SqlSessionFactory;

[if !supportLists]2、  [endif]通过原始的XMLConfigBuilder 构建configuration添加拦截器;

源码就不黏贴了;


项目中思路的应用

Plugin的作用就是将项目中的执行对象某些方法暴露在外面,允许第三方在方法执行之前,执行自己的逻辑;相对来说将自己的逻辑暴露在外面还是有很大的风险,目前自己想到的有参数效验,自己原来封装了一个jar,里面有一些类似@MAX @MIN等等注解,这个时候,可以向外提供plugin,到达不修改jar的前提,又向外提供添加注解的能力;

你可能感兴趣的:(plugin的实现和原理(mybatis3.4.6))