前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MyBatis源码阅读(五) ---Mapper接口的获取过程

MyBatis源码阅读(五) ---Mapper接口的获取过程

作者头像
终有救赎
发布2023-12-26 09:21:50
1240
发布2023-12-26 09:21:50
举报
文章被收录于专栏:多线程多线程
一、简介

前面一篇文章我们总结了SqlSession的创建过程,既然会话已经创建成功,那么下一步自然就是去获取到Mapper接口执行具体的SQL,当然也可以直接使用SqlSession执行具体的SQL。本篇文章主要分析一下Mapper接口的获取过程。

二、Mapper接口的获取过程
代码语言:javascript
复制
//获取Mapper接口
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

通过sqlSession获取mapper接口,实现上是从configuration对象中获取,具体代码如下:

代码语言:javascript
复制
//org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper
@Override
public <T> T getMapper(Class<T> type) {
  //type ====> interface com.wsh.mybatis.mybatisdemo.mapper.UserMapper
  return configuration.<T>getMapper(type, this);
}
 
//org.apache.ibatis.session.Configuration#getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  //实际上是从成员变量knownMappers中获取的,knownMappers是个key-value形式的缓存,key是mapper接口的class对象,
  //value是MapperProxyFactory代理工厂,MapperProxyFactory就是用来创建MapperProxy代理类的
  return mapperRegistry.getMapper(type, sqlSession);
}

跟踪代码,来到MapperRegistry的getMapper()方法:

代码语言:javascript
复制
public class MapperRegistry {
 
  private final Configuration config;
  //knownMappers是个key-value形式的缓存
  //key是mapper接口的class对象,
  //value是MapperProxyFactory代理工厂
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
    //org.apache.ibatis.binding.MapperRegistry#getMapper
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      //从knownMappers的Map里根据mapper接口取出对应的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 {
        //调用MapperProxyFactory代理工厂的newInstance()方法真正对mapper接口进行代理
        return mapperProxyFactory.newInstance(sqlSession);
      } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
      }
    }
}

Debug如下图,可以看到从knownMappers的Map里根据mapper接口取出对应的MapperProxyFactory工厂类:

图片.png
图片.png

会不会有小伙伴们不知道,mapper接口是如何存放进中的?如果不是很清楚的话,可以先看看【MyBatis源码阅读(三) --- 配置信息的解析以及SqlSessionFactory构建过程】这一篇文章,里面有详细介绍。

下面我们来看看MapperProxyFactory是如何代理我们的mapper接口的,具体代码在mapperProxyFactory.newInstance(sqlSession)。

代码语言:javascript
复制
//org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)
public T newInstance(SqlSession sqlSession) {
  //构造一个MapperProxy代理
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  //动态代理获取Mapper接口实例
  return newInstance(mapperProxy);
}
 
 //实际上底层就是使用的JDK动态代理,对Mapper接口进行代理
//熟悉JDK动态代理的同学看到这个代码应该不陌生
protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

可以看到newInstance方法里面看到了熟悉的代码,即JDK动态代理的实现,我们的mapper接口就在这里被代理。Debug看下各个属性的值:

图片.png
图片.png

接下来,我们看看MapperProxy类的代码:

代码语言:javascript
复制
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;
 
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
}

实现了InvocationHandler接口,也就是说,当我们调用mapper接口对应方法的时候,实际上会执行invoke方法。

如下图,可以看到我们获取的UserMapper其实是Mybatis帮我们代理之后返回的是一个实例:

图片.png
图片.png

到这里,我们的mapper接口就获取到了,拿到的是一个代理过后的mapper接口。

三、Mapper接口的获取流程图
图片.png
图片.png
四、总结

简单总结一下mapper接口的获取流程:

  • 1、从configuration对象中获取mapper接口;
  • 2、根据type去mapperRegistry中查找mapper接口,实际上是从mapperRegistry的knownMappers的属性中根据接口全限类名去查找对应的MapperProxyFactory;
  • 3、MapperProxyFactory使用JDK动态代理实现对mapper接口的代理,返回代理之后的对象;

好了,mapper接口的获取流程就先分析到这里。鉴于笔者水平有限,如果文章有什么错误或者需要补充的,希望小伙伴们指出来,希望这篇文章对大家有帮助。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-12-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、简介
  • 二、Mapper接口的获取过程
  • 三、Mapper接口的获取流程图
  • 四、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档