最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mybatis的Mapper代理對象生成及調(diào)用過程示例詳解

 更新時間:2023年09月14日 09:00:55   作者:福  
這篇文章主要為大家介紹了Mybatis的Mapper代理對象生成及調(diào)用過程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

導讀

你在mapper.xml文件中寫的sql語句最終是怎么被執(zhí)行的?我們編寫的mapper接口最終是怎么生成代理對象并被調(diào)用執(zhí)行的?

這部分內(nèi)容應該是Mybatis框架中最關(guān)鍵、也是最復雜的部分,今天文章的主要目標是要搞清楚:

  • mapper.xml文件是怎么初始化到Mybatis框架中的?
  • mapper接口生成動態(tài)代理對象的過程。
  • mapper接口動態(tài)代理對象的執(zhí)行過程。

掌握了以上3個問題,我們就掌握了Mybatis的核心。

Mapper初始化過程

指的是mapper.xml文件的解析過程。

這個動作是在SqlSessionFactory創(chuàng)建的過程中同步完成的,或者說是在SqlSessionFactory被build出來之前完成。

XMLMapperBuilder負責對mapper.xml文件做解析,SqlSessionFactorBean的buildSqlSessionFactory()方法中會針對不同配置情況進行解析。其中我們最常用的是在配置文件中指定mapper.xml文件的路徑(就是源碼中的這個mapperLocations):

if (this.mapperLocations != null) {
      if (this.mapperLocations.length == 0) {
        LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
      } else {
        for (Resource mapperLocation : this.mapperLocations) {
          if (mapperLocation == null) {
            continue;
          }
          try {
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
            xmlMapperBuilder.parse();
          } catch (Exception e) {
            throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
          } finally {
            ErrorContext.instance().reset();
          }
          LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
    }
    return this.sqlSessionFactoryBuilder.build(targetConfiguration);

創(chuàng)建XMLMapperBuilder對象并調(diào)用parse()方法完成解析。

XMLMapperBuilder#configurationElement()

parse方法會調(diào)用configurationElement()方法,對mapper.xml的解析的關(guān)鍵部分就在configurationElement方法中。

我們今天把問題聚焦在mapper.xml文件中sql語句的解析,也就是其中的insert、update、delete、select等標簽的解析。

private void configurationElement(XNode context) {
    try {
      //獲取namespace
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.isEmpty()) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
    //二級緩存Ref解析
      cacheRefElement(context.evalNode("cache-ref"));
   //二級緩存配置的解析
      cacheElement(context.evalNode("cache"));
  //parameterMap標簽的解析 
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
   //resultMap標簽的解析 
resultMapElements(context.evalNodes("/mapper/resultMap"));
   //sql標簽的解析 
sqlElement(context.evalNodes("/mapper/sql"));
    //關(guān)鍵部分:sql語句的解析 
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

對sql語句解析部分繼續(xù)跟蹤會發(fā)現(xiàn),最終sql語句解析完成之后會創(chuàng)建MappedStatement并保存在configuration對象中(以xml文件中的id為key值的Map中):

MappedStatement statement = statementBuilder.build();
    configuration.addMappedStatement(statement);
    return statement;

這樣我們就明白了,mapper.xml中編寫的sql語句被解析后,最終保存在configuration對象的mappedStatements中了,mappedStatements其實是一個以mapper文件中相關(guān)標簽的id值為key值的hashMap。

Mapper接口生成動態(tài)代理過程

我們都知道Mapper對象是通過SqlSession的getMapper方法獲取到的,其實Mapper接口的代理對象也就是在這個調(diào)用過程中生成的:

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

調(diào)用Configuration的getMapper方法:

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

調(diào)用mapperRegistry的getMapper方法,首先從knownMappers(以namespace為key值保存mapperProxyFactory的HashMap)中獲取到mapperProxyFactory,mapperProxyFactory人如其名,就是mapper代理對象工廠,負責創(chuàng)建mapper代理對象。

獲取到mapperProxyFactory之后,調(diào)用newInstance方法:

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

調(diào)用MapperProxy的newInstance方法:

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<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

典型的JDK動態(tài)代理生成邏輯,傳入的回調(diào)參數(shù)為mapperProxy對象,也就是說我們對Mapper接口的方法調(diào)用,最終通過生成的動態(tài)代理對象,會調(diào)用到這個回調(diào)對象mapperProxy的invoke方法。

至此,mapper接口動態(tài)代理的生成邏輯我們就從源碼的角度分析完畢,現(xiàn)在我們已經(jīng)搞清楚mapper動態(tài)代理的生成過程,最重要的是,我們知道m(xù)apper接口的方法調(diào)用最終會轉(zhuǎn)換為對mapperProxy的invoke方法的調(diào)用。

mapper接口動態(tài)代理對象的執(zhí)行過程

這個問題現(xiàn)在已經(jīng)明確了,其實就是MapperProxy的invoke方法。

對MapperProxy稍加研究,我們發(fā)現(xiàn)他的invoke方法最終會調(diào)用MapperMethod的execute方法:

public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      return mapperMethod.execute(sqlSession, args);
    }

execute方法根據(jù)調(diào)用方法的sql類型分別調(diào)用sqlSession的insert、update、delete、select方法,相應的方法會根據(jù)調(diào)用方法名從configuration中匹配MappedStatement從而執(zhí)行我們在mapper.xml中配置的sql語句(參考XMLMapperBuilder#configurationElement()部分)。

因此我們也就明白了為什么mapper.xml文件中配置的sql語句的id必須要對應mapper接口中的方法名,因為Mybatis要通過mapper接口中的方法名去匹配sql語句、從而最終執(zhí)行該sql語句!

任務完成!

其實雖然我們知道SqlSession默認的落地實現(xiàn)對象是DefaultSqlSession,我們在mapper.xml中編寫的sql語句其實是DefaultSqlSession負責執(zhí)行的,但是MapperMethod中sqlSession其實也是代理對象(DefaultSqlSession的代理對象),所以說Mybatis中到處都是動態(tài)代理,這部分我們下次再分析。

以上就是Mybatis的Mapper代理對象生成及調(diào)用過程示例詳解的詳細內(nèi)容,更多關(guān)于Mybatis Mapper代理對象生成調(diào)用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java高級用法之注解和反射講義

    java高級用法之注解和反射講義

    這篇文章主要給大家介紹了關(guān)于java高級用法之注解和反射講義的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-05-05
  • springboot創(chuàng)建文件夾失敗的解決

    springboot創(chuàng)建文件夾失敗的解決

    這篇文章主要介紹了springboot創(chuàng)建文件夾失敗的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • 最新IntelliJ IDEA 2021版配置 Tomcat 8.5 的詳細步驟

    最新IntelliJ IDEA 2021版配置 Tomcat 8.5 的詳細步驟

    idea開發(fā)工具一直是java環(huán)境最好用,很受廣大開發(fā)者喜愛,今天通過本文給大家分享最新IntelliJ IDEA 2021版配置 Tomcat 8.5 的詳細步驟,本文通過圖文并茂的形式給大家介紹的非常詳細,需要的朋友可以參考下
    2021-06-06
  • Java中使用阻塞隊列控制線程集實例

    Java中使用阻塞隊列控制線程集實例

    這篇文章主要介紹了Java控制阻塞隊列線程集實例,本文用一個程序展示了如何使用阻塞隊列來控制線程集,程序功能是在一個目錄及它的所有子目錄下搜索所有文件,打印出包含指定關(guān)鍵字的文件列表,需要的朋友可以參考下
    2015-01-01
  • 29個要點幫你完成java代碼優(yōu)化

    29個要點幫你完成java代碼優(yōu)化

    本文給大家分享的是個人總結(jié)的29個java優(yōu)化需要注意的地方,非常的全面細致,推薦給大家,有需要的小伙伴可以參考下
    2015-03-03
  • 基于Spring AI+Milvus的RAG混合檢索的實戰(zhàn)指南

    基于Spring AI+Milvus的RAG混合檢索的實戰(zhàn)指南

    這篇文章主要給大家記錄了從零搭建企業(yè)級 RAG 知識庫問答系統(tǒng)的工程,涵蓋意圖路由、混合檢索、RRF 融合、query 改寫、rerank 精排全鏈路,并通過代碼示例講解的非常詳細,需要的朋友可以參考下
    2026-06-06
  • java反射實現(xiàn)javabean轉(zhuǎn)json實例代碼

    java反射實現(xiàn)javabean轉(zhuǎn)json實例代碼

    基于java反射機制實現(xiàn)javabean轉(zhuǎn)json字符串實例,大家參考使用吧
    2013-12-12
  • Spring @Cacheable自定義緩存過期時間的實現(xiàn)示例

    Spring @Cacheable自定義緩存過期時間的實現(xiàn)示例

    本文主要介紹了Spring @Cacheable自定義緩存過期時間的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-05-05
  • 最新hadoop安裝教程及hadoop的命令使用(親測可用)

    最新hadoop安裝教程及hadoop的命令使用(親測可用)

    這篇文章主要介紹了最新hadoop安裝教程(親測可用),本文主要講解了如何安裝hadoop、使用hadoop的命令及遇到的問題解決,需要的朋友可以參考下
    2022-06-06
  • MybatisPlus中靜態(tài)工具DB的實現(xiàn)

    MybatisPlus中靜態(tài)工具DB的實現(xiàn)

    本文主要介紹了使用MybatisPlus的Db靜態(tài)工具類來避免Service之間的循環(huán)依賴問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-12-12

最新評論

邹城市| 水城县| 双柏县| 右玉县| 上杭县| 南投县| 桐柏县| 苏尼特左旗| 鲁甸县| 林州市| 五峰| 万荣县| 丰镇市| 无锡市| 江北区| 台南市| 瑞昌市| 海伦市| 万安县| 云霄县| 泗阳县| 来凤县| 资阳市| 云阳县| 荣成市| 徐汇区| 黄大仙区| 靖远县| 黔东| 方正县| 临清市| 中卫市| 安陆市| 阳东县| 永顺县| 偏关县| 桂平市| 鸡西市| 西充县| 杂多县| 江口县|