Mybatis源码分析十之MapperProxy

一、MapperProxyFactory

在使用mybatis的时候,配置好对应文件,只需要调用接口方法就可以实现对数据库操作。接口本身是不可以直接使用的,需要有具体的实现类,在mybatis中我们不需要去编写具体的实现类,只需要编写对应的接口就行。到这里,估计大家都已猜出,mybatis用代理的方式为我们做了实现,所以本文会重点分析mybatis中代理的实现。

在SqlSession中有个getMapper(Class< T > type),我们只需要传递一个具体的接口,就可以得到其代理对象。其方法的具体调用链在:

configuration.getMapper(type, this);
//configuration对象中又调用了
mapperRegistry.getMapper(type, sqlSession)

所以getMapper方法的具体实现在MapperRegistry

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  //先从knownMappers集合中取出对应的MapperProxyFactory对象
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
    //调用其实例化方法
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

在分析Configuration解析的时候,就已经分析了knownMappers,集合中保存的是接口和MapperProxyFactory对应关系。从类名称就可知这是实例化MapperProxy的工厂对象,查看其newInstance(sqlSession)方法:

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

这里会把当前的SqlSession对象和mapper接口封装成MapperProxy对象,然后在调用动态代理的Proxy.newProxyInstance方法,返回mapper接口的代理对象。

二、MapperProxy

既然是动态代理,MapperProxy对象必然实现了InvocationHandler接口,具体的代理逻辑也会在这个对象中,所以具体的逻辑我们只需要查看其invoke方法就行:

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
    //如果当前是一个Object对象,直接执行其方法
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else {
      //这里会把当前调用的方法封装成一个MapperMethodInvoker然后再调用其invoke方法
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

cachedInvoker方法:

   private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      return MapUtil.computeIfAbsent(methodCache, method, m -> {
        if (m.isDefault()) {//判断是否是一个默认方法(接口中定义的)
          try {
            if (privateLookupInMethod == null) {
              return new DefaultMethodInvoker(getMethodHandleJava8(method));
            } else {
              return new DefaultMethodInvoker(getMethodHandleJava9(method));
            }
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException
              | NoSuchMethodException e) {
            throw new RuntimeException(e);
          }
        } else {
        //正常走这里
          return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
  }

所以会调用到PlainMethodInvoker的invoke方法

    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      return mapperMethod.execute(sqlSession, args);
    }

到这里,我们发现最后执行的就是MapperMethod的execute方法,这个方法比较简单根据当前执行的是增删改查哪一个,然后再具体调用SqlSession对象中对应的方法即可,所以最终的调用都在SqlSession中。

三、总结

Mybatis采用动态代理模式,实现了只需要编写接口,然后调用接口方法就可以实现对数据库的操作,底层的实现是通过MapperProxy对象,通过层层封装调用,最后都会映射到SqlSession的调用上来。

以上,有任何不对的地方,请指正,敬请谅解。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜鸟+1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值