1. 程式人生 > >(十一)Mybatis Mapper動態代理

(十一)Mybatis Mapper動態代理

注:程式碼已託管在GitHub上,地址是:https://github.com/Damaer/Mybatis-Learning ,專案是mybatis-07-MapperDynamicProxy,需要自取,需要配置maven環境以及mysql環境,覺得有用可以點個小星星,小菜鳥在此Thanks~
在之前的程式碼中我們的執行過程再梳理一下,首先我們執行Test,呼叫dao介面方法


介面的定義:

呼叫介面的實現類方法:

最後才是呼叫真正的sql:

那麼我們想是不是可以減少一步呢?我們不用自己實現介面,只需要將介面的名字和mapper檔案的namespace對應起來,將接口裡面的方法名與sql語句標籤的id對應起來是不是就可以了呢?事實上,mybatis提供了這樣的做法,這就是mapper動態代理。


mapper動態代理的例子

首先主配置檔案(Mybatis.xml),在裡面配置資料庫連線資訊,註冊需要掃描的mapper檔案:

定義資料庫查詢的介面,裡面每一個介面的名字很重要,需要和mapper裡面每一條sql對應起來:

定義mapper檔案(namespace要寫我們定義的介面,而每條sql的id與我們的介面方法名字對應起來):

那我們在使用的時候:

我們在前面還寫到過一個selectStudentMap方法,但是裡面呼叫的是和SelectList一樣的sql,在介面的實現類裡面我們自己處理了一下,但是現在使用自動實現的話,底層只會呼叫SelectOne或者SelectList方法,所以這個方法會報錯,如果接受型別是list,那麼框架會自動使用selectList方法,否則就會選擇selectOne()這個方法。
在這裡我們使用的是返回的是map,所以自動選擇返回selectOne()方法,那麼就會報錯。如果我們需要使用自動返回map的話,可以自己定一個map,或者返回list之後再處理,這個知識點後面再介紹,有興趣可以訪問:

mybatis的mapper返回map結果集

mapper動態代理的原理

打一個斷點在sqlSession.getMapper()方法上:

我們可以看到執行下面的介面方法(介面SqlSession的方法)

<T> T getMapper(Class<T> var1);

這是一個介面,我們可以看到實現介面的有兩個類,一個是DefaultSqlSession,一個是SqlSessionManager,我們需要看的是DefaultSqlSession下面的介面:

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

我們知道,在建立sqlsession的時候,confiiguration這個配置物件已經建立完成。跟進去,這是使用mapper註冊器物件的getMapper方法,將當前的sqlSession物件傳遞進去:

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

我們跟進去原始碼,可以發現裡面使用knownMappers.get(type)來獲取mapper代理工廠,這個konwnMappers是一個hashMap,這個hashMap裡面已經初始化了mapperProxyFactory物件了,獲取到工廠物件之後,再去使用sqlSession例項化:

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);
    }
  }

例項化的時候,使用了mapper動態代理:

public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

從下面的debug結果中我們可以看到,這是動態代理的結果: