编程语言
首页 > 编程语言> > 【MyBatis源码解析】Mapper是如何获得?

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

作者:互联网

【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