欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

mybatis源码探究(-)MapperProxyFactory&MapperProxy

程序员文章站 2022-06-30 19:07:09
在MyBatis中MapperProxyFactory,MapperProxy,MapperMethod是三个很重要的类。 弄懂了这3个类你就大概清楚Mapper接口与SQL的映射, 为什么是接口,没有实例类也可以完成注入或者调用。 其中MapperMethod可以参考:MapperMethod源码 ......

在mybatis中mapperproxyfactory,mapperproxy,mappermethod是三个很重要的类。

弄懂了这3个类你就大概清楚mapper接口与sql的映射,

为什么是接口,没有实例类也可以完成注入或者调用

其中mappermethod可以参考:mappermethod源码分析传送门

在调用mybatis的addmapper的时候如果你跟踪源码就会最终跟到mapperregistry的addmapper中有如下的语句:

knownmappers.put(type, new mapperproxyfactory<t>(type));

 

type就是mapper接口。下面我们来看一下mapperproxyfactory的源码。

mapperproxyfactory

public class mapperproxyfactory<t> {

  private final class<t> mapperinterface;
  private map<method, mappermethod> methodcache = new concurrenthashmap<method, mappermethod>();

  public mapperproxyfactory(class<t> mapperinterface) {
    this.mapperinterface = mapperinterface;
  }

  public class<t> getmapperinterface() {
    return mapperinterface;
  }

  public map<method, mappermethod> getmethodcache() {
    return methodcache;
  }

  @suppresswarnings("unchecked")
  protected t newinstance(mapperproxy<t> mapperproxy) {
    return (t) proxy.newproxyinstance(mapperinterface.getclassloader(), new class[] { mapperinterface }, mapperproxy);
  }

  public t newinstance(sqlsession sqlsession) {
    final mapperproxy<t> mapperproxy = new mapperproxy<t>(sqlsession, mapperinterface, methodcache);
    return newinstance(mapperproxy);
  }
}

 

mapperproxyfactory一看名字我们就知道肯定是一个工厂类,就是为了生成mapperproxy。其实mapperproxyfactory也非常简单。首先看2个成员mapperinterface就是mapper接口,methodcache就是对mapper接口中的方法和方法的封装类(mappermethod)的映射。mappermethod处理的事情主要就是:处理mapper接口中方法的注解,参数,和返回值。如果想了解更多的细节可以参考mappermethod源码分析传送门

然后就是2个newinstance,看名字就知道是工厂方法,一个是protected,一个是public,所以首先看public方法。发现有一个参数sqlsession,sqlsession处理的其实就是执行一次sql的过程。其实public的newinstance就是new了一个mapperproxy,关于mapperproxy可以先看一下后面的mapperproxy。然后调用了protected的newinstance。

接着我们看protected的newinstance。protected简单明了,就是使用java proxy的工厂方法生成一个了mapper接口的代理类。我想都关系mybatis源码了应该对java的proxy动态代理方式应该非常熟悉了。如果不熟悉可以参考一下我之前写的一篇关于java动态代理的java动态代理细探

我们指定mapperproxy实现了invocationhandler,所以调用mapper接口中的方法走的是mapperproxy的invoke。而mapperproxy的invoke是把method包装成了mappermethod,mappermethod处理了mapper接口方法与xml映射的关系。是不是串联起来了。

mapperproxy

public class mapperproxy<t> implements invocationhandler, serializable {

  private static final long serialversionuid = -6424540398559729838l;
  private final sqlsession sqlsession;
  private final class<t> mapperinterface;
  private final map<method, mappermethod> methodcache;

  public mapperproxy(sqlsession sqlsession, class<t> mapperinterface, map<method, mappermethod> methodcache) {
    this.sqlsession = sqlsession;
    this.mapperinterface = mapperinterface;
    this.methodcache = methodcache;
  }

  public object invoke(object proxy, method method, object[] args) throws throwable {
    if (object.class.equals(method.getdeclaringclass())) {
      try {
        return method.invoke(this, args);
      } catch (throwable t) {
        throw exceptionutil.unwrapthrowable(t);
      }
    }
    final mappermethod mappermethod = cachedmappermethod(method);
    return mappermethod.execute(sqlsession, args);
  }

  private mappermethod cachedmappermethod(method method) {
    mappermethod mappermethod = methodcache.get(method);
    if (mappermethod == null) {
      mappermethod = new mappermethod(mapperinterface, method, sqlsession.getconfiguration());
      methodcache.put(method, mappermethod);
    }
    return mappermethod;
  }

}

我们看mapperproxy实现了invocationhandler接口,不用仔细想,基本上是因为java动态代理。

既然实现了invocationhandler接口那么当然要先看一下invoke方法了。首先检查了如果是object中方法就直接调用方法本身。

如果不是就把方法method包装成mappermethod,我们前面已经提到了mappermethod主要就是处理方法的注解,参数,返回值,参数与sql语句中的参数的对应关系。再次打一波广告,如果像了解更多mappermethod的细节可以参考mappermethod源码分析传送门

因为把method处理为mappermethod还是一个比较重的操作,所以这里做了缓存处理。

小结

总结一下,我们公共mybatis添加的mapper的操作实际上添加的是mapperproxyfactory,这个是mapperproxy的工厂类,但是mapperproxyfactory生产的也不是mapperproxy,而是mapper的proxy代理。使用的invocationhandler是mapperproxy,mapperproxy的invoke方法实际上是把mapper接口方法包装为了mappermethod,并执行的mappermethod的execute方法。mappermethod处理的逻辑是mapper方法与xml中的sql的一些映射关系。例如@mapkey等注解的处理,一些如rowbounds特殊参数的处理以及一些其他关于mapper方法与sql映射的处理。执行sql的逻辑其实是委托给了sqlsession相关的逻辑。