您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關如何從原理上理解MyBatis對Spring源碼的擴展實現,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
今天我們大概從以下幾點去講解MyBatis對于Spring的一個擴展思路!
首先我們至少要知道一個事情,就是FactoryBean的一個大致結構:
可以看到,整個 FactoryBean有三個方法:
此時,至少我們已經知道了,我們可以通過一個FactoryBean來生產一個對象,可以獲取這個對象的類型以及這個對象是不是單例!但是離開了Spring它就什么也不是,那么Spring封裝這個東西是干嘛的呢?
正是因為FactoryBean的存在我們才能夠插手或者改變一個Bean的創建過程!,為什么這么說呢?我舉個例子:
就拿大家常用的MyBatis為例,我們都知道MyBatis的使用一般都是使用一個接口,映射一個XML文件,MyBatis內部經過動態代理,動態的為接口生成一個實現類,從而讓我們能夠通過接口直接調用里面的邏輯!
但是MyBatis通過Spring管理之后,同學們是否疑惑過,我們明明沒有使用MyBatis那一套邏輯,僅僅通過一個@Autowired
注解,就能夠直接注入到Service使用,那么MyBatis的動態代理邏輯大概是在哪里做的?
沒錯就是在FactoryBean里面做的!
熟悉MyBatis用法的同學看到這個代碼是不是就十分的熟悉了?這一段正是MyBatis通過接口生成動態代理的一段邏輯!那么此時我們至少知道了Spring能夠FactoryBean調用 getObject()方法能夠創建一個對象,并把對象管理起來!
這個為什么呢?作者的想法是,正是因為Spring的作者想要放權給使用者,讓使用者自己實現創建一個bean的邏輯,所以Spring并不會過多的插手該Bean的實例化過程,使得一個Bean的實例化完全又使用者本人去實現!
這個類并不會像其它普通的bean那樣在Spring容器初始化的時候就進行實例化,而是會類似于懶加載的一種機制,再獲取的時候才會進行創建和返回!至于是不是單例,要取決于isSingleton()方法的返回值!
當然,這個創建出來的bean也會被緩存,AOP等邏輯也會對該類生效,當然這都是后話!
相信上述文章看完之后你對Factory會有一個基本的認識,我們總結以下Spring調用它的基本流程!
Spring只是一個項目管理的框架,他也是由JAVA語言編寫的,所以它必須遵循JAVA語法的規范!我們能夠使用Spring幫助我們管理我們開發過程中的一些類,能夠自動注入或者AOP代理等邏輯!
但是我們是否發現,Spring它只能夠管理我們指定的包下的類,或者我們手動添加的一些類!而且Spring也沒有辦法去幫我們掃描一些抽象類或者接口,但是我們有時候因為一些特殊的開發,我們必須要打破Spring原有的掃描過程,比如我們就要Spring幫我們管理一個接口、幫我們掃描一些加了特定注解的類等特殊需求,這個時候,我們就不能夠使用Spring為我們提供的掃描邏輯了,需要我們自定義一個掃描邏輯!
舉個例子(我們還是以MyBatis為例):
我們通過上面FactoryBean的學習我們理解了一件事,Spring中MyBatis能夠通過FactoryBean
進行動態代理的創建并返回,但是我們都知道使用jdk動態代理所必須的一個元素:接口
,因為jdk動態代理就是基于接口來做的!
這些接口從哪里來呢?要知道Spring是不會把接口也掃描的,所以此時就需要我們的自定義掃描器了,我們使用自定義掃描器將接口掃描到,然后通過修改BeanDefinition
強行指定為FactoryBean類型的bean, 把我們的接口傳入進去,然后再將BeanDefinition
加入bean工廠,此時我們需要的一個必須元素接口
就有了!
ImportBeanDefinitionRegistrar
也是Spring生命周期中重要的一環,上周我們學到,Spring再執行BeanFactoryPostProcessor
時,會實現執行系統內置的一個后置處理器---ConfigurationClassPostProcessor
,它的作用就是掃描項目指定路徑下的類,轉換成對應的BeanDefinition
!但是它的作用可不止這一個哦!
它除了有掃描指定包下的類的功能,還有解析@Import
注解的功能,ImportBeanDefinitionRegistrar
就是@Import
中一個比較特殊的類,它會被Spring自動的回調內部的registerBeanDefinitions()
方法!
那么由此可知它的調用時機再ConfigurationClassPostProcessor之后
,剩余其他的所有BeanFactoryPostProcessor之前
!
上面我們也說到了,他會回調registerBeanDefinitions()
方法,那么意義何在呢?如果只是能夠進行回調的話,BeanDefinitionRegistryPostProcessor
也能完成類似的功能,它的特殊之處在于什么呢?我們看一下它的方法簽名!
我們重點關注第一個參數,他在回調的時候,會將標注@Import
注解的類的所有的元信息封裝成AnnotationMetadata
類,攜帶回去!
那么攜帶回去有什么意義呢?舉個例子,依舊以MyBatis為例!
我們試想以下,上面我們說呢,我們可以通過自定義掃描器將一個個接口轉換成FactoryBean然后交給Spring管理,但是我們要掃描那個包下的類呢?
使用過Spring整合MyBatis的人都應該知道,我們一般都會在啟動類上標注一個注解
@MapperScan
指定Mapper接口的包路徑,它的目的就是為了向registerBeanDefinitions
方法傳遞掃描的路徑,以此完成掃描!
雖然這個BeanDefinitionRegistryPostProcessor
上周復習的時候,我做過大量的源碼層面的講解!但是今天依舊要簡單說一下!
上周的學習我們知道BeanDefinitionRegistryPostProcessor
是BeanFactoryPostProcessor
的子類,他們兩個有什么區別嗎?
我們要知道,BeanFactoryPostProcessor
只能夠對已經存在的 BeanDefinition
進行修改,但是沒有辦法進行添加和刪除,但是BeanDefinitionRegistryPostProcessor
不一樣,他對父類進行了擴展,提供了添加和刪除的API,我們可以通過該類進行增加和刪除bean工廠的BeanDefinition
!
我們依舊是以MyBatis為例!
我們此時通過自定義掃描器把接口轉換成了一個bd,但是我們要如何向Spring工廠添加我們掃描到的Bd呢?就是使用這個BeanDefinitionRegistryPostProcessor
來進行注冊bean定義!
我相信,通過上面的關鍵點的講解,你現在心里應該有了一個差不多的概念!MyBatis擴展Spring的方式大概如下:
首先我們需要在配置類標注一個注解MapperScan
,并且傳入Mapper接口所在包路徑!
MapperScan
會通過@Import
注解向Spring注入一個MapperScannerRegistrar
類,他是ImportBeanDefinitionRegistrar
類型的,會被Spring自動回調registerBeanDefinitions
方法!
MapperScannerRegistrar
的registerBeanDefinitions
方法會構建一個類型為MapperScannerConfigurer
的BeanDefinition
,他是BeanDefinitionRegistryPostProcessor
類型的!然后注冊進Spring容器里面!
Spring生命周期會自動回調MapperScannerConfigurer
的postProcessBeanDefinitionRegistry
方法!
postProcessBeanDefinitionRegistry
方法內部創建了一個自定義的掃描器ClassPathMapperScanner
,掃描你傳入的包路徑下的所有的接口,并轉換為BeanDefinition
!
獲取到所有指定接口的BeanDefinition
之后,遍歷所有的BeanDefinition
,然后修改他的BeanClass
為MapperFactoryBean
類,他是FactoryBean
類型的!
設置完BeanClass之后,通過definition.getPropertyValues().add()
方法,傳入該BeanDefinition
代表的接口!
將所有的BeanDefinition
通過 6、7步驟設置之后,全部注冊到bean工廠中!由BeanFactory對這些FactoryBean進行管理,和生命周期的管理!
注意,此時這些類并沒有被實例化,被實例化的是你傳入的FactoryBean
類,真實的類還沒有被實例化!
FactoryBean
的時候,會調用
getObjectType
方法,將返回值與你要使用的接口類型作比對!FactoryBean
的
getObject
方法將對象創建出來!jdk動態代理
,完成MyBatis的代理邏輯!isSingleton
方法的返回值判斷,如果是單例對象,就將該對象緩存起來!并返回!至此,我們完成了整個MyBatis整合Spring的全部過程!
在MyBatis內部是如何自定義掃描器的呢?而且還能打破Spring原有的掃描流程,將接口掃描進項目!
整段代碼大致分為兩部分:
毋庸置疑,他是創建了一個Mybatis自己的掃描器,這個掃描器是ClassPathBeanDefinitionScanner
子類,這也是Spring為我們提供的擴展點之一,我們可以基于該掃描器,擴展任意的類變成bd,當然,他需要符合我們的預設規則!什么是預設規則呢?我們可以看到在我圈的第一個紅框里面似乎做了一個注冊的操作,注冊的什么呢?
通常情況下該判斷就都是為true的,所以這里會執行一個添加的邏輯,添加到哪里了呢?
它添加到了一個集合里面!至此,我們至少知道了,這里會向集合里面添加一個過濾器,至于有什么用,我們后面會說到,你這里先記住!
我們再看第二個紅框,開始執行掃描操作了!具體里面的代碼我就不粘貼了,他會調用父類的掃描邏輯,我們直接看父類是如何做的!
這里將包路徑轉換為對應的bd,如何做的呢?
這么長的邏輯,我們重點關注兩個判斷:
BeanDefinition
,還記得我們剛剛注冊的那個過濾器嗎?一個過濾器被添加進集合里面了,他就是在這里被使用的!因為那個過濾器的定義所以這里一定會返回為true!m所以我們第一個判斷過了!一個類別轉換成了BeanDefinition
isCandidateComponent
方法,這里是判斷一個類到底需不需要被添加進集合里面返回,我們常識得知,Spring是不會替我們管理一個接口類的,但是Mapper類又偏偏是一個接口,所以這時MyBatis不得不改寫原有的邏輯使得它支持掃描接口并轉換為bd,我們看下里面的邏輯!因為MyBatis的Mapper類是一個接口,所以這里會返回為true! 所以我們第二個判斷進去了,一個接口的BeanDefinition
被添加進集合!并返回!
至此,我們大概知道了掃描器的工作原理!我們看一下將接口掃描到之后做了那些操作呢?
他會循環遍歷所有掃描到的接口bd,向每一個bd的構造方法傳遞一個值,他是當前bd所代表的接口的全限定名!
上面介紹MyBatis擴展FactoryBean的時候說到!它通過jdk創建動態代理,但是接口時哪里來的?就是通過
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
注入進去的!我們都知道Spring創建對象是基于definition
創建的,所以,我們可以通過definition
來注入我們想要注入的值,他常用的用法還有類似下面的:
MyBatis 中正是使用構造函數 的方式注入了一個接口的值!
強行將接口的類型轉換為FactoryBean類型的!
他是為了延遲初始化,使用jdk動態代理返回一個對象!從而完成MyBatis的功能!
看完上述內容,你們對如何從原理上理解MyBatis對Spring源碼的擴展實現有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。