这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战
时序图
sequenceDiagram
participant S as Client
participant A as DefaultSqlSession
participant B as Configuration
participant C as MapperRegistry
participant D as MapperProxyFactory
participant E as MapperProxy
S ->> A : getMapper
A ->> B : getMapper
B ->> C : getMapper
C ->> D : newInstance
D ->> E : new
E -->> D : MapperProxy
D -->> S : ProxyInstance
详细步骤
Client
//获取Mapper接口的代理对象
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
DefaultSqlSession#getMapper
@Override
public <T> T getMapper(Class<T> type) {
/**
* 1.configuration对象为创建DefaultSqlSessionFactory时通过解析配置文件而创建的
* 2.openSession时,会将configuration对象作为构造参数传入
*/
return configuration.getMapper(type, this);
}
Configuration#getMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
/**
* 通过Mapper类型与sqlsession获取Mapper对象,将{@link Configuration#addMapper(java.lang.Class)}保存的MapperProxyFactory对象取出
*/
return mapperRegistry.getMapper(type, sqlSession);
}
MapperRegistry#getMapper
addMapper方法介绍见文章Mybatis源码之mappers标签解析
/**
* 找到指定的映射文件,并根据映射文件信息为该映射接口生成一个代理实现
* @param type 映射接口
* @param sqlSession sqlSession
* @param <T> 映射接口类型
* @return 代理实现对象
*/
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 找出指定的接口的代理工厂
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 创建对应代理器的实例
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
MapperProxyFactory#newInstance
// 生成一个 MapperProxy对象
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
// 创建MapperProxy代理实例
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
// 创建mapperInterface接口的代理对象
return newInstance(mapperProxy);
}
MapperProxy
/**
* MapperProxy基于动态代理将针对映射接口的方法调用转接成了对MapperMethod对象execute方法的调用,
* 即只要用对应的 MapperProxy对象作为映射接口的 实现,便可以完整地实现为映射接口接入数据库操作的功能
*
* @author Clinton Begin
* @author Eduardo Macarron
*/
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
// methodCache 属性维护接口方法和 MapperMethod 对象的对应关系
// 该Map的键为方法,值为MapperMethod对象,通过该属性,完成MapperProxy内(即映射接口内)方法和MapperMethod的绑定
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
}
以上便是Mybatis获取Mapper实例过程。