【Mybatis】Mybatis源码之获取Mapper实例

979 阅读1分钟

这是我参与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实例过程。