亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何從原理上理解MyBatis對Spring源碼的擴展實現

發布時間:2021-12-18 14:59:37 來源:億速云 閱讀:182 作者:柒染 欄目:大數據

今天就跟大家聊聊有關如何從原理上理解MyBatis對Spring源碼的擴展實現,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

今天我們大概從以下幾點去講解MyBatis對于Spring的一個擴展思路!

如何從原理上理解MyBatis對Spring源碼的擴展實現

一、FactoryBean是干什么?

首先我們至少要知道一個事情,就是FactoryBean的一個大致結構:

如何從原理上理解MyBatis對Spring源碼的擴展實現      
FactoryBean的大致結構

可以看到,整個 FactoryBean有三個方法:

  • getObject(): 返回具體創建的真實對象!
  • getObjectType(): 返回創建對象的類型!
  • isSingleton(): 創建的該對象是不是單例對象!

此時,至少我們已經知道了,我們可以通過一個FactoryBean來生產一個對象,可以獲取這個對象的類型以及這個對象是不是單例!但是離開了Spring它就什么也不是,那么Spring封裝這個東西是干嘛的呢?

     

1. 自定義Spring實例化的bean

正是因為FactoryBean的存在我們才能夠插手或者改變一個Bean的創建過程!,為什么這么說呢?我舉個例子:

就拿大家常用的MyBatis為例,我們都知道MyBatis的使用一般都是使用一個接口,映射一個XML文件,MyBatis內部經過動態代理,動態的為接口生成一個實現類,從而讓我們能夠通過接口直接調用里面的邏輯!

但是MyBatis通過Spring管理之后,同學們是否疑惑過,我們明明沒有使用MyBatis那一套邏輯,僅僅通過一個@Autowired注解,就能夠直接注入到Service使用,那么MyBatis的動態代理邏輯大概是在哪里做的?

沒錯就是在FactoryBean里面做的!

如何從原理上理解MyBatis對Spring源碼的擴展實現      
MyBatis使用FactoryBean進行動態代理

熟悉MyBatis用法的同學看到這個代碼是不是就十分的熟悉了?這一段正是MyBatis通過接口生成動態代理的一段邏輯!那么此時我們至少知道了Spring能夠FactoryBean調用 getObject()方法能夠創建一個對象,并把對象管理起來!

     

2. 不遵循Spring的生命周期

這個為什么呢?作者的想法是,正是因為Spring的作者想要放權給使用者,讓使用者自己實現創建一個bean的邏輯,所以Spring并不會過多的插手該Bean的實例化過程,使得一個Bean的實例化完全又使用者本人去實現!

這個類并不會像其它普通的bean那樣在Spring容器初始化的時候就進行實例化,而是會類似于懶加載的一種機制,再獲取的時候才會進行創建和返回!至于是不是單例,要取決于isSingleton()方法的返回值!

當然,這個創建出來的bean也會被緩存,AOP等邏輯也會對該類生效,當然這都是后話!

     

3. FactoryBean的總結

相信上述文章看完之后你對Factory會有一個基本的認識,我們總結以下Spring調用它的基本流程!

如何從原理上理解MyBatis對Spring源碼的擴展實現      
FactoryBean的調用流程
     

二、自定義掃描器

Spring只是一個項目管理的框架,他也是由JAVA語言編寫的,所以它必須遵循JAVA語法的規范!我們能夠使用Spring幫助我們管理我們開發過程中的一些類,能夠自動注入或者AOP代理等邏輯!

但是我們是否發現,Spring它只能夠管理我們指定的包下的類,或者我們手動添加的一些類!而且Spring也沒有辦法去幫我們掃描一些抽象類或者接口,但是我們有時候因為一些特殊的開發,我們必須要打破Spring原有的掃描過程,比如我們就要Spring幫我們管理一個接口、幫我們掃描一些加了特定注解的類等特殊需求,這個時候,我們就不能夠使用Spring為我們提供的掃描邏輯了,需要我們自定義一個掃描邏輯!

     

1. 栗子

舉個例子(我們還是以MyBatis為例):

我們通過上面FactoryBean的學習我們理解了一件事,Spring中MyBatis能夠通過FactoryBean進行動態代理的創建并返回,但是我們都知道使用jdk動態代理所必須的一個元素:接口,因為jdk動態代理就是基于接口來做的!

這些接口從哪里來呢?要知道Spring是不會把接口也掃描的,所以此時就需要我們的自定義掃描器了,我們使用自定義掃描器將接口掃描到,然后通過修改BeanDefinition強行指定為FactoryBean類型的bean, 把我們的接口傳入進去,然后再將BeanDefinition加入bean工廠,此時我們需要的一個必須元素接口就有了!

如何從原理上理解MyBatis對Spring源碼的擴展實現      
自定義掃描器結合FactoryBean
     

三、ImportBeanDefinitionRegistrar

     

1. 調用時機

ImportBeanDefinitionRegistrar也是Spring生命周期中重要的一環,上周我們學到,Spring再執行BeanFactoryPostProcessor時,會實現執行系統內置的一個后置處理器---ConfigurationClassPostProcessor,它的作用就是掃描項目指定路徑下的類,轉換成對應的BeanDefinition!但是它的作用可不止這一個哦!

它除了有掃描指定包下的類的功能,還有解析@Import注解的功能,ImportBeanDefinitionRegistrar就是@Import中一個比較特殊的類,它會被Spring自動的回調內部的registerBeanDefinitions()方法!

那么由此可知它的調用時機再ConfigurationClassPostProcessor之后剩余其他的所有BeanFactoryPostProcessor之前

     

2. 回調方法以及意義

上面我們也說到了,他會回調registerBeanDefinitions()方法,那么意義何在呢?如果只是能夠進行回調的話,BeanDefinitionRegistryPostProcessor也能完成類似的功能,它的特殊之處在于什么呢?我們看一下它的方法簽名!

如何從原理上理解MyBatis對Spring源碼的擴展實現      
image-20200914224036880

我們重點關注第一個參數,他在回調的時候,會將標注@Import注解的類的所有的元信息封裝成AnnotationMetadata類,攜帶回去!

那么攜帶回去有什么意義呢?舉個例子,依舊以MyBatis為例!

我們試想以下,上面我們說呢,我們可以通過自定義掃描器將一個個接口轉換成FactoryBean然后交給Spring管理,但是我們要掃描那個包下的類呢?

使用過Spring整合MyBatis的人都應該知道,我們一般都會在啟動類上標注一個注解@MapperScan指定Mapper接口的包路徑,它的目的就是為了向registerBeanDefinitions方法傳遞掃描的路徑,以此完成掃描!

如何從原理上理解MyBatis對Spring源碼的擴展實現      
image-20200914225321751
     

四、BeanDefinitionRegistryPostProcessor

     

1. 概念

雖然這個BeanDefinitionRegistryPostProcessor上周復習的時候,我做過大量的源碼層面的講解!但是今天依舊要簡單說一下!

上周的學習我們知道BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor的子類,他們兩個有什么區別嗎?

我們要知道,BeanFactoryPostProcessor只能夠對已經存在的 BeanDefinition進行修改,但是沒有辦法進行添加和刪除,但是BeanDefinitionRegistryPostProcessor不一樣,他對父類進行了擴展,提供了添加和刪除的API,我們可以通過該類進行增加和刪除bean工廠的BeanDefinition!

     

2.舉個例子

我們依舊是以MyBatis為例!

我們此時通過自定義掃描器把接口轉換成了一個bd,但是我們要如何向Spring工廠添加我們掃描到的Bd呢?就是使用這個BeanDefinitionRegistryPostProcessor來進行注冊bean定義!

如何從原理上理解MyBatis對Spring源碼的擴展實現      
BeanDefinitionRegistryPostProcessor
     

五、MyBatis如何擴展的Spring呢?

     

1. 擴展步驟(初始化步驟)

我相信,通過上面的關鍵點的講解,你現在心里應該有了一個差不多的概念!MyBatis擴展Spring的方式大概如下:

  1. 首先我們需要在配置類標注一個注解MapperScan,并且傳入Mapper接口所在包路徑!

  2. MapperScan會通過@Import注解向Spring注入一個MapperScannerRegistrar類,他是ImportBeanDefinitionRegistrar類型的,會被Spring自動回調registerBeanDefinitions方法!

  3. MapperScannerRegistrarregisterBeanDefinitions方法會構建一個類型為MapperScannerConfigurerBeanDefinition ,他是BeanDefinitionRegistryPostProcessor類型的!然后注冊進Spring容器里面!

  4. Spring生命周期會自動回調MapperScannerConfigurerpostProcessBeanDefinitionRegistry方法!

  5. postProcessBeanDefinitionRegistry方法內部創建了一個自定義的掃描器ClassPathMapperScanner,掃描你傳入的包路徑下的所有的接口,并轉換為BeanDefinition !

  6. 獲取到所有指定接口的BeanDefinition之后,遍歷所有的BeanDefinition,然后修改他的BeanClassMapperFactoryBean類,他是FactoryBean類型的!

  7. 設置完BeanClass之后,通過definition.getPropertyValues().add()方法,傳入該BeanDefinition代表的接口!

  8. 將所有的BeanDefinition通過 6、7步驟設置之后,全部注冊到bean工廠中!由BeanFactory對這些FactoryBean進行管理,和生命周期的管理!

    注意,此時這些類并沒有被實例化,被實例化的是你傳入的FactoryBean類,真實的類還沒有被實例化!

     

2. 擴展步驟(實例化步驟)

  1. 在使用或者獲取這些bean的時候,Spring會首先獲取你要使用的接口類型!
  2. 遍歷當前容器內所有的bean逐個對比,當有匹配的直接返回!但是,因為Mapper接口還并沒有被實例化!所以并沒有找到,所以在遍歷到         FactoryBean的時候,會調用         getObjectType方法,將返回值與你要使用的接口類型作比對!
  3. 當 FactoryBean的返回類型匹配的時候,Spring會調用         FactoryBean的         getObject方法將對象創建出來!
  4. 創建過程中,通過之前傳入的接口,做         jdk動態代理,完成MyBatis的代理邏輯!
  5. 對象創建完成后,通過         isSingleton方法的返回值判斷,如果是單例對象,就將該對象緩存起來!并返回!

至此,我們完成了整個MyBatis整合Spring的全部過程!

     

3.源碼重點講解

     
1)自定義掃描器

在MyBatis內部是如何自定義掃描器的呢?而且還能打破Spring原有的掃描流程,將接口掃描進項目!

如何從原理上理解MyBatis對Spring源碼的擴展實現      
image-20200915215932029

整段代碼大致分為兩部分:

  1. 毋庸置疑,他是創建了一個Mybatis自己的掃描器,這個掃描器是ClassPathBeanDefinitionScanner子類,這也是Spring為我們提供的擴展點之一,我們可以基于該掃描器,擴展任意的類變成bd,當然,他需要符合我們的預設規則!什么是預設規則呢?我們可以看到在我圈的第一個紅框里面似乎做了一個注冊的操作,注冊的什么呢?

    如何從原理上理解MyBatis對Spring源碼的擴展實現          
    image-20200915220235102

通常情況下該判斷就都是為true的,所以這里會執行一個添加的邏輯,添加到哪里了呢?

如何從原理上理解MyBatis對Spring源碼的擴展實現      
image-20200915220432302

它添加到了一個集合里面!至此,我們至少知道了,這里會向集合里面添加一個過濾器,至于有什么用,我們后面會說到,你這里先記住!

  1. 我們再看第二個紅框,開始執行掃描操作了!具體里面的代碼我就不粘貼了,他會調用父類的掃描邏輯,我們直接看父類是如何做的!

    如何從原理上理解MyBatis對Spring源碼的擴展實現          
    image-20200915220818278


    這里將包路徑轉換為對應的bd,如何做的呢?

如何從原理上理解MyBatis對Spring源碼的擴展實現      
image-20200915221123343

這么長的邏輯,我們重點關注兩個判斷:

  • 第一個判斷,會判斷該類是否被過濾,到底該不該轉換為         BeanDefinition,還記得我們剛剛注冊的那個過濾器嗎?一個過濾器被添加進集合里面了,他就是在這里被使用的!
如何從原理上理解MyBatis對Spring源碼的擴展實現      
image-20200915221801594

因為那個過濾器的定義所以這里一定會返回為true!m所以我們第一個判斷過了!一個類別轉換成了BeanDefinition

  • 第二個判斷,會調用子類的         isCandidateComponent方法,這里是判斷一個類到底需不需要被添加進集合里面返回,我們常識得知,Spring是不會替我們管理一個接口類的,但是Mapper類又偏偏是一個接口,所以這時MyBatis不得不改寫原有的邏輯使得它支持掃描接口并轉換為bd,我們看下里面的邏輯!
如何從原理上理解MyBatis對Spring源碼的擴展實現      
image-20200915221458473

因為MyBatis的Mapper類是一個接口,所以這里會返回為true!  所以我們第二個判斷進去了,一個接口的BeanDefinition被添加進集合!并返回!

至此,我們大概知道了掃描器的工作原理!我們看一下將接口掃描到之后做了那些操作呢?

     
2)通過BeanDefinition操作創建流程
如何從原理上理解MyBatis對Spring源碼的擴展實現      
image-20200915222512900
  • 他會循環遍歷所有掃描到的接口bd,向每一個bd的構造方法傳遞一個值,他是當前bd所代表的接口的全限定名!

    上面介紹MyBatis擴展FactoryBean的時候說到!它通過jdk創建動態代理,但是接口時哪里來的?就是通過

     definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
           

    注入進去的!我們都知道Spring創建對象是基于definition創建的,所以,我們可以通過definition來注入我們想要注入的值,他常用的用法還有類似下面的:

    如何從原理上理解MyBatis對Spring源碼的擴展實現          
    image-20200915223229818

    MyBatis 中正是使用構造函數 的方式注入了一個接口的值!

    如何從原理上理解MyBatis對Spring源碼的擴展實現          
    image-20200915223354790
  • 強行將接口的類型轉換為FactoryBean類型的!

    他是為了延遲初始化,使用jdk動態代理返回一個對象!從而完成MyBatis的功能!

看完上述內容,你們對如何從原理上理解MyBatis對Spring源碼的擴展實現有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

连城县| 桂平市| 阳高县| 财经| 丹凤县| 云阳县| 阳原县| 行唐县| 嵩明县| 霍城县| 浮梁县| 大连市| 双柏县| 天等县| 丹阳市| 六盘水市| 进贤县| 屏东市| 天台县| 胶南市| 彭水| 长汀县| 西昌市| 自贡市| 黄石市| 和平区| 大渡口区| 大英县| 锡林郭勒盟| 松江区| 涟水县| 新巴尔虎右旗| 广汉市| 涡阳县| 曲周县| 乐陵市| 邹平县| 邹城市| 科技| 延津县| 西吉县|