【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