欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mybatis源碼解析之mapper接口的代理模式詳解

 更新時(shí)間:2023年12月25日 11:47:43   作者:二狗家有礦  
這篇文章主要介紹了Mybatis源碼解析之mapper接口的代理模式詳解,在mybatis中執(zhí)行sql時(shí)有兩種方式,一種是基于statementId,也就是直接調(diào)用SqlSession的方法,需要的朋友可以參考下

一、簡介

在mybatis中執(zhí)行sql時(shí)有兩種方式,一種是基于statementId,也就是直接調(diào)用SqlSession的方法,如sqlSession.update(“statementId”); 還有一種方法是基于java接口,也是日常開發(fā)中最常用的方式。 mapper接口中的每個(gè)方法都可以喝mapper xml中的一條sql語句對(duì)應(yīng),我們可以直接通過調(diào)用接口方法的方式進(jìn)行sql執(zhí)行。因?yàn)閙ybatis會(huì)為mapper接口通過jdk動(dòng)態(tài)代理的方法生成接口的實(shí)現(xiàn)類,本篇文章將針對(duì)mapper接口的代理展開分析。 以下是mapper接口的代理模式的核心組件的類圖。

Mapper代理模式類圖

二、MapperRegistry

MapperRegistry通過Map結(jié)構(gòu)的屬性knownMappers中維護(hù)著mybatis中所有的mapper接口。

1. MapperRegistry#addMapper(class)

當(dāng)開發(fā)者在配置文件中配置了通過mappers節(jié)點(diǎn)的子節(jié)點(diǎn)mapper配置了mapper接口時(shí),會(huì)調(diào)用configuation#addMapper(Class)記錄mapper接口,而configuration又委托了MapperRegistry#addMapper(class)處理邏輯。

public <T> void addMapper(Class<T> type) {
  if (type.isInterface()) {
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
      knownMappers.put(type, new MapperProxyFactory<T>(type));
      // It's important that the type is added before the parser is run
      // otherwise the binding may automatically be attempted by the
      // mapper parser. If the type is already known, it won't try.
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

可以看到,MapperRegistry以mapper接口的類型為key值,將接口類型封裝成MapperProxyFactory作為value值放入knownMappers。

2. MapperRegistry#getMapper(Class, SqlSession)

該方法基于SqlSession參數(shù)向外提供了對(duì)應(yīng)類型的mapper接口的對(duì)象。

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

根據(jù)mapper接口類型找到對(duì)應(yīng)的MapperProxyFactory時(shí),調(diào)用其newInstance方法得到對(duì)應(yīng)的對(duì)象返回。

三、MapperProxyFactory

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

如果對(duì)jdk動(dòng)態(tài)代理有一定了解,很容易就能看出來MapperProxyFactory的newInstance方法是很典型的生成代理對(duì)象的方式。

四、MapperProxy

MapperProxy作為InvocationHandler的實(shí)現(xiàn)類,是jdk動(dòng)態(tài)代理模式的核心。

1. MapperProxy#invoke(Object, Method, Object[])

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else if (isDefaultMethod(method)) {
      return invokeDefaultMethod(proxy, method, 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;
}

對(duì)于Object方法和default方法,執(zhí)行原方法邏輯即可。 對(duì)于對(duì)于sql語句的方法,交給MapperMethod 處理。

五、MapperMethod

Mapper類負(fù)責(zé)去處理mapper接口中的sql方法。

public Object execute(SqlSession sqlSession, Object[] args) {
	 Object result;
	 switch (command.getType()) {
	   case INSERT: {
	 	Object param = method.convertArgsToSqlCommandParam(args);
	     result = rowCountResult(sqlSession.insert(command.getName(), param));
	     break;
	   }
	   case UPDATE: {
	     Object param = method.convertArgsToSqlCommandParam(args);
	     result = rowCountResult(sqlSession.update(command.getName(), param));
	     break;
	   }
	   case DELETE: {
	     Object param = method.convertArgsToSqlCommandParam(args);
	     result = rowCountResult(sqlSession.delete(command.getName(), param));
	     break;
	   }
	   case SELECT:
	     if (method.returnsVoid() && method.hasResultHandler()) {
	       executeWithResultHandler(sqlSession, args);
	       result = null;
	     } else if (method.returnsMany()) {
	       result = executeForMany(sqlSession, args);
	     } else if (method.returnsMap()) {
	       result = executeForMap(sqlSession, args);
	     } else if (method.returnsCursor()) {
	       result = executeForCursor(sqlSession, args);
	     } else {
	       Object param = method.convertArgsToSqlCommandParam(args);
	       result = sqlSession.selectOne(command.getName(), param);
	     }
	     break;
	   case FLUSH:
	     result = sqlSession.flushStatements();
	     break;
	   default:
	     throw new BindingException("Unknown execution method for: " + command.getName());
	 }
	 if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
	   throw new BindingException("Mapper method '" + command.getName() 
	       + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
	 }
	 return result;
}

MapperMethod還有一個(gè)靜態(tài)內(nèi)部類MethodSignature用作mapper方法的標(biāo)簽,SqlCommand用作sql命令。 可以看到,根據(jù)sql命令的類型(insert|update|delete|select|flush)和返回類型分別調(diào)用SqlSession的不同方法,然后對(duì)insert|update|delete方法的返回值做適配。

MapperMethod#rowCountResult(int)

private Object rowCountResult(int rowCount) {
 final Object result;
  if (method.returnsVoid()) {
    result = null;
  } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
    result = rowCount;
  } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
    result = (long)rowCount;
  } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
    result = rowCount > 0;
  } else {
    throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
  }
  return result;
}

insert|update|delete方法方法的返回值就是sql命令的匹配行數(shù),而在mapper方法中支持Integer、Long、Boolean和void類型的返回,因此做簡單適配。

到此這篇關(guān)于Mybatis源碼解析之mapper接口的代理模式詳解的文章就介紹到這了,更多相關(guān)mapper接口的代理模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java數(shù)據(jù)結(jié)構(gòu)基礎(chǔ):緒論

    java數(shù)據(jù)結(jié)構(gòu)基礎(chǔ):緒論

    這篇文章主要介紹了Java的數(shù)據(jù)解構(gòu)基礎(chǔ),希望對(duì)廣大的程序愛好者有所幫助,同時(shí)祝大家有一個(gè)好成績,需要的朋友可以參考下,希望能給你帶來幫助
    2021-07-07
  • java中棧和隊(duì)列的實(shí)現(xiàn)和API的用法(詳解)

    java中棧和隊(duì)列的實(shí)現(xiàn)和API的用法(詳解)

    下面小編就為大家?guī)硪黄猨ava中棧和隊(duì)列的實(shí)現(xiàn)和API的用法(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • SpringBoot整合Dozer映射框架流程詳解

    SpringBoot整合Dozer映射框架流程詳解

    dozer是用來兩個(gè)對(duì)象之間屬性轉(zhuǎn)換的工具,有了這個(gè)工具之后,我們將一個(gè)對(duì)象的所有屬性值轉(zhuǎn)給另一個(gè)對(duì)象時(shí),就不需要再去寫重復(fù)的set和get方法了,下面介紹下SpringBoot中Dozer的使用,感興趣的朋友一起看看吧
    2022-07-07
  • Java將Word文件轉(zhuǎn)為OFD文件

    Java將Word文件轉(zhuǎn)為OFD文件

    大家好,本篇文章主要講的是Java將Word文件轉(zhuǎn)為OFD文件,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Java的JSON格式轉(zhuǎn)換庫GSON的初步使用筆記

    Java的JSON格式轉(zhuǎn)換庫GSON的初步使用筆記

    GSON是Google開發(fā)并在在GitHub上開源的Java對(duì)象與JSON互轉(zhuǎn)功能類庫,在Android開發(fā)者中也大受歡迎,這里我們就來看一下Java的JSON格式轉(zhuǎn)換庫GSON的初步使用筆記:
    2016-06-06
  • Java 排序算法整合(冒泡,快速,希爾,拓?fù)?,歸并)

    Java 排序算法整合(冒泡,快速,希爾,拓?fù)洌瑲w并)

    這篇文章主要介紹了Java 排序算法整合(冒泡,快速,希爾,拓?fù)?,歸并),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Mybatis配置之<properties>屬性配置元素解析

    Mybatis配置之<properties>屬性配置元素解析

    這篇文章主要介紹了Mybatis配置之<properties>屬性配置元素解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 如何使用IDEA的groovy腳本文件生成帶JPA注解的實(shí)體類(圖文詳解)

    如何使用IDEA的groovy腳本文件生成帶JPA注解的實(shí)體類(圖文詳解)

    這篇文章主要介紹了如何使用IDEA的groovy腳本文件生成帶JPA注解的實(shí)體類,本文通過圖文并茂實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • idea鼠標(biāo)控制放大縮小的操作

    idea鼠標(biāo)控制放大縮小的操作

    這篇文章主要介紹了idea鼠標(biāo)控制放大縮小的操作教程,具有很好的參考價(jià)值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • SpringBoot實(shí)現(xiàn)對(duì)Http接口進(jìn)行監(jiān)控的代碼

    SpringBoot實(shí)現(xiàn)對(duì)Http接口進(jìn)行監(jiān)控的代碼

    Spring Boot Actuator是Spring Boot提供的一個(gè)模塊,用于監(jiān)控和管理Spring Boot應(yīng)用程序的運(yùn)行時(shí)信息,本文將介紹一下Spring Boot Actuator以及代碼示例,以及如何進(jìn)行接口請(qǐng)求監(jiān)控,需要的朋友可以參考下
    2024-07-07

最新評(píng)論