Mybatis 源碼分析:獲取 Mapper 接口對象

我們知道使用 mybatis 作爲 ORM 框架時,想要使用面向接口的方式操作數據庫,即使用 mapper 文件形式,那麼就需要獲取 Mapper 接口對象,從而才能對數據庫進行操作。那麼問題來了,在 java 中是不可能對 interface 進行 new 的,那麼 mybatis 是怎麼做到面向 Mapper 接口的呢?那就從源碼的角度揭開這層其實沒有想象那麼高深的面紗!
首先來看看 SqlSession(默認實現 DefaultSqlSession) 類的 getMapper() 方法。

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

非常簡單,它把獲取 mapper 接口的對象邏輯委託給了 Configuration 類的 getMapper() 方法。

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

這看起來似乎有那麼點意思了,它從 mapperRegistry 中去獲取 mapper 接口對象,並且還把 SqlSession 傳遞過去。看過我之前的文章應該清楚 mapperRegistry 是什麼東西,從字面意思來看,它就是 Mapper 註冊中心,它的定義是:

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

那麼接下來應該是到了揭開面紗的門口了,我們看看 MapperRegistry 類的 getMapper() 方法又幹了啥呢?

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 {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

可以看到,首先會從 knownMappers 獲取 Mapper 接口對應的 MapperProxyFactory 對象,那 knownMappers 又是什麼呢,在之前的文章有說到,它就是在構建 SqlSessionFactory 對象時,對 xml 配置文件解析後把所有的 mapper 接口都會放入在 knownMappers 這個 HashMap 中去,key 爲 mapper 接口的全限定名,value 爲 MapperProxyFactory 對象。knownMappers 字段的定義爲:

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

然後,會使用 MapperProxyFactory 類的 newInstance() 方法,此時就是揭開面紗見證奇蹟的時刻到了,看看這個方法到底幹了啥

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

這裏首先生成了一個 MapperProxy 對象,那這個對象又是啥呢,字面意思看起來是 Mapper 的代理對象,這個稍等說。方法之後又掉用了一個重載的 newInstance() 方法:

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

千呼萬喚始出來,終於見到了最核心的一行代碼,它使用了 jdk 自帶的動態代理機制對 Mapper 接口生成了一個代理對象,在這裏可以看到 MapperProxy 是作爲 Proxy.newProxyInstance() 方法的第三個參數傳遞進去的,所以可以想象到 MapperProxy 肯定實現了 jdk 的 InvocationHandler 接口。

public class MapperProxy<T> implements InvocationHandler, Serializable {
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  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;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  	// ...
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }
}

現在,我們就知道了獲取 Mapper 接口對象的整個流程,至於之後對其進行方法調用的過程,且看下回分解~~~
最後,老規矩附上整個過程的時序圖:
獲取 mapper 接口對象

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章