ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

【MyBatis源码解析】Mapper是如何获得?

2021-01-22 10:33:30  阅读:147  来源: 互联网

标签:Mapper getMapper SqlSession MapperProxyFactory sqlSession 源码 MyBatis type Class


【MyBatis源码解析】Mapper是如何获得?

前言

1.以往链接

2.myBatis经典运行流程

希望读者们能将这个基础的流程熟读于心

String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
}

// TODO 流程图

一、mapper整体流程

场景驱动一下,当你第一次去向sqlSession请求获得mapper实例的时候,myBatis是怎样的一个流程。

sqlSession.getMapper(Class Type)
->Configuration.getMapper(Class Type, SqlSession )
->mapperRegistry.getMapper(type, sqlSession)
->mapperProxyFactory.newInstance(sqlSession)->mapperProxy

// TODO流程图

二、源码走一遍mapper获取流程

1.SqlSession.getMapper(Class)

此处会调用SqlSession的实现类DefaultSqlSession.getMapper()

@Override
public <T> T getMapper(Class<T> type) {
  return configuration.<T>getMapper(type, this);
}

此处的configuration在构造DefaultSqlSession的时候会加载进来

public class DefaultSqlSession implements SqlSession {

  private final Configuration configuration;
......
  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

2.Configuration.getMapper(Class, SqlSession)

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return mapperRegistry.getMapper(type, sqlSession);
}

Configuration中的字段mapperRegistry,在构造Configuration的时候就创建了MapperRegistry。

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

3.MapperRegistry.getMapper(type, sqlSession)

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 查看之前有没有对应的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构造一个mapper
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}

knownMappers,每个Mapper.class对应一个MapperProxyFactory

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

第一次进来这里的时候,一定有人会好奇,kuownMappers必然是没有数据的,它很是进行初始化呢?

这个问题我们后续再进行讨论。

我们假设有这个MapperProxyFactory的存在。就会进入到mapperProxyFactory.newInstance(sqlSession);

//TODO 何时将knownMappers初始化。

4.MapperProxyFactory.newInstance(sqlSession)->mapperProxy

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

mapperProxy是InvocationHandler接口的代理类,最后实现JDK动态代理创建了Mapper对象

protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

但我们查看MapperProxy的时候会发现,它是没有Mapper的实例对象的,所以当运行mapper方法的时候,它是怎样查数据库的呢?下一节会进行探讨

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;

// TODO 解释MapperProxy

三、讲解特别之处

1.何时将knownMappers初始化。

先说结论,这开始是始于XMLMapperBuilder.bindMapperForNamespace。然后它的调用链是一条闭环。

<-org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parse
<-MapperRegistry.addMapper
<-Configuration.addMapper
<-XMLMapperBuilder.bindMapperForNamespace
<-MapperAnnotationBuilder.loadXmlResource
<-org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parse
<-org.apache.ibatis.binding.MapperRegistry#addMapper

结束于org.apache.ibatis.binding.MapperRegistry#addMapper。就是成功put,key为该类型的Class和MapperProxyFactory为止。

if (hasMapper(type)) {
  throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
  public <T> boolean hasMapper(Class<T> type) {
    return knownMappers.containsKey(type);
  }

MapperProxyFactory真正创建的地方。org.apache.ibatis.binding.MapperRegistry#addMapper

knownMappers.put(type, new MapperProxyFactory<>(type));

简单场景驱动一下,第一次进来的时会新建一个关于该type的MapperProxyFactory。然后去MapperAnnotationBuilder.loadXmlResource检查标志。Spring可能不知道实际的资源名称,因此我们检查一个标志以防止再次加载资源。

为啥会有个循环,是防止资源加载错误的doubleCheck。

2.解释MapperProxy

标签:Mapper,getMapper,SqlSession,MapperProxyFactory,sqlSession,源码,MyBatis,type,Class
来源: https://www.cnblogs.com/zhoujianyi/p/14311921.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有