Mybatis源碼解析之MapperProxy

[上一篇]:Mybatis源碼解析之配置解析

從菜菜的Mybatis源碼解析之Spring獲取Mapper過程中知道了Spring與MyBatis如何連接起來的,這篇菜菜將介紹MyBatis的重中之重MapperProxy

問題

為什么MyBatis只用寫接口不用寫實(shí)現(xiàn)就可以運(yùn)行起來?

思考

既然Spring與MyBatis連接起來了,那么通過MyBatis中的這些Mapper接口注入的類都是由Spring進(jìn)行管理的,所以需要知道Spring中這些Mapper接口對(duì)應(yīng)的Bean到底是什么。

揭秘答案-源碼

Mybatis源碼解析之Spring獲取Mapper過程這篇里說了設(shè)置了這些Mapper接口的class對(duì)象或是類的全限定名為MapperFactoryBean ,這里再貼出源碼

 // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
   definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
   definition.setBeanClass(this.mapperFactoryBean.getClass());

下面需要看下MapperFactoryBean的源碼

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  private Class<T> mapperInterface;

  private boolean addToConfig = true;

  public MapperFactoryBean() {
    //intentionally empty 
  }
  
  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
  @Override
  protected void checkDaoConfig() {
    super.checkDaoConfig();

    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        configuration.addMapper(this.mapperInterface);
      } catch (Exception e) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
        throw new IllegalArgumentException(e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }

 //主要看這個(gè)方法
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
  @Override
  public Class<T> getObjectType() {
    return this.mapperInterface;
  }

  @Override
  public boolean isSingleton() {
    return true;
  }
  public void setMapperInterface(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
  public Class<T> getMapperInterface() {
    return mapperInterface;
  }
  public void setAddToConfig(boolean addToConfig) {
    this.addToConfig = addToConfig;
  }
  public boolean isAddToConfig() {
    return addToConfig;
  }
}

從MapperFactoryBean源碼中看出,它繼承了SqlSessionDaoSupport并實(shí)現(xiàn)了Spring中的FactoryBean,SqlSessionDaoSupport是一個(gè)抽象的支持類,用來為你提供SqlSession,將在Mybatis源碼解析之SqlSession來自何方
中講解。因?yàn)閷?shí)現(xiàn)了FactoryBean接口所以MapperFactoryBean是一個(gè)FactoryBean。

我們知道Spring實(shí)例化一個(gè)類有兩個(gè)時(shí)機(jī),一個(gè)是在初始化的時(shí)候getBean實(shí)例化,第二個(gè)是對(duì)設(shè)置了lazy-init屬性的在第一次向Spring索要Bean的時(shí)候getBean實(shí)例化,最終都是調(diào)用Spring中的getBean方法

MyBatis中設(shè)置Mapper接口的class對(duì)象為MapperFactoryBean,而MapperFactoryBean又是一個(gè)FactoryBean所以在實(shí)例化調(diào)用getBean最終會(huì)調(diào)用MapperFactoryBean中的getObject()方法,將改方法返回的對(duì)象作為實(shí)例化Bean,我們先看下getObeject的調(diào)用鏈(初始化是從refresh中的finishBeanFactoryInitialization開始進(jìn)入preInstantiateSingletons再開始的,需要一層一層看),再看getObject()的方法

image.png
public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
}

這里的getSqlSession()會(huì)得到SqlSessionTemplate,為什么是SqlSessionTemplate,詳見Mybatis源碼解析之SqlSession來自何方

//SqlSessionDaoSupport里的方法,返回的是SqlSessionTemplate
 public SqlSession getSqlSession() {
    return this.sqlSession;
  }

查看SqlSessionTemplate中的getMapper源碼,是從Configuration中g(shù)etMapper

//SqlSessionTemplate中部分方法
 @Override
  public <T> T getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
  }
@Override
  public Configuration getConfiguration() {
    return this.sqlSessionFactory.getConfiguration();
  }

Configuration中獲取Mapper源碼,又是從mapperRegistry獲取的,mapperRegistry相當(dāng)于mapper的注冊(cè)中心,這里能獲取到所有的mapper信息

//Configuration 中g(shù)etMapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
 }

MapperRegistry中g(shù)etMapper,看到這里答案快浮出水面了,通過MapperProxy的工廠MapperProxyFactory創(chuàng)建代理對(duì)象

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

MapperProxyFactory的具體實(shí)現(xiàn)源碼

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

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

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

答案至此終于揭開,最終在Spring中這些接口對(duì)應(yīng)的對(duì)象為JDK動(dòng)態(tài)代理生成的Mapper代理類MapperProxy。我們?cè)诔绦蛑心玫降腗apper對(duì)象也是MapperProxy。我們繼續(xù)看下MapperProxy的源代碼

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;

  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 {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    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進(jìn)行數(shù)據(jù)庫的相關(guān)操作時(shí)會(huì)調(diào)用MapperProxy的invoke()方法,該方法先判斷接口方法是否有實(shí)現(xiàn)類,如果有,調(diào)用實(shí)現(xiàn)類的方法,如果沒有(MyBatis的Mapper沒有實(shí)現(xiàn)類)就調(diào)用MapperMethod的execute方法
通過看MapperMethod源碼發(fā)現(xiàn)最終還是調(diào)用sqlSession中的相關(guān)方法,sqlSession再委托給Excutor去執(zhí)行,關(guān)于Excutor菜菜還沒寫相關(guān)文章,請(qǐng)自行搜索

如有錯(cuò)誤,歡迎各位大佬斧正!
[下一篇]:Mybatis源碼解析之SqlSession來自何方

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容