Spring整合Mybatis框架方法剖析
Spring整合其他框架的核心思路
就是將其他框架生成的類放到Spring容器中。同理,Spring整合Mybatis也是讓Mybatis生成的Mapper接口的代理對象作為Bean注冊到Spring容器中。
解決的核心問題
- 1)通過FactoryBean創(chuàng)建Mapper接口代理對象,并且指定構(gòu)造方法參數(shù)為Mapper接口class,并且設(shè)置BeanDefinition.setAutowireMode(AUTOWIRE_BY_TYPE)。
- 2)通過ImportBeanDefinitionRegistrar或者BeanDefinitionRegistryPostProcessor中創(chuàng)建的掃描器實現(xiàn)對多個Mapper接口代理對象的創(chuàng)建和注冊。
- 3)掃描器篩選類時要重寫isCandidateComponent(),第一個isCandidateComponent()總是返回true(因為添加一個TypeFilter,其match方法總是返回true。),第二個isCandidateComponent()如果為接口時返回true。
ClassPathMapperScanner#registerFilters這里面添加一個總是返回true的TypeFilter:
if (acceptAllInterfaces) { // default include filter that accepts all classes addIncludeFilter((metadataReader, metadataReaderFactory) -> true); }
關(guān)于Mapper接口生成對象
- 1.必須是一個對象(代理對象,JDK動態(tài)代理Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy))
- 2.這個對象必須實現(xiàn)Mapper接口(動態(tài)代理對象必然實現(xiàn)Mapper接口)
- 這兩條由mybatis保證
- 3.必須在spring容器當(dāng)中
這條由mybatis-spring保證
1)FactoryBean,采用的就是這種方式,控制對象的生成過程,生成JDK動態(tài)代理對象
2)ac.getBeanFactory().registerSingleton(),也是一樣麻煩
3)@Bean 方式,針對每一個接口都要一個@Bean,太麻煩
4)@Service 這樣是把類交給Spring,然后創(chuàng)建對象,但是這里是接口,創(chuàng)建對象會報錯,我們這里需要控制對象的生成過程(生成動態(tài)代理)
Mybatis-Spring 1.3.2版本底層源碼執(zhí)行流程
- 1)通過@MapperScan導(dǎo)入了MapperScannerRegistrar類
- 2)MapperScannerRegistrar類實現(xiàn)了ImportBeanDefinitionRegistrar接口,所以Spring在啟動時會調(diào)用MapperScannerRegistrar類中的registerBeanDefinitions方法
- 3)在registerBeanDefinitions方法中定義了一個ClassPathMapperScanner對象,用來掃描mapper
- 4)設(shè)置ClassPathMapperScanner對象(繼承自ClassPathBeanDefinitionScanner)可以掃描到接口,因為在Spring中是不會掃描接口的
- 5)同時因為ClassPathMapperScanner中重寫了isCandidateComponent方法,導(dǎo)致isCandidateComponent只會認(rèn)為接口是備選者Component
- 6)通過利用Spring的掃描后,會把接口掃描出來并且得到對應(yīng)的BeanDefinition
- 7)接下來把掃描得到的BeanDefinition進行修改,把BeanClass修改為MapperFactoryBean,把AutowireMode修改為byType
- 8)掃描完成后,Spring就會基于BeanDefinition去創(chuàng)建Bean了,相當(dāng)于每個Mapper對應(yīng)一個FactoryBean
- 9)在MapperFactoryBean中的getObject方法中,調(diào)用了getSqlSession()去得到一個sqlSession對象,然后根據(jù)對應(yīng)的Mapper接口生成一個Mapper接口代理對象,這個代理對象就成為Spring容器中的Bean
- 10)sqlSession對象是Mybatis中的,一個sqlSession對象需要SqlSessionFactory來產(chǎn)生
- 11)MapperFactoryBean的AutowireMode為byType,所以Spring會自動調(diào)用set方法,有兩個set方法,一個setSqlSessionFactory,一個setSqlSessionTemplate,而這兩個方法執(zhí)行的前提是根據(jù)方法參數(shù)類型能找到對應(yīng)的bean,所以Spring容器中要存在SqlSessionFactory類型的bean或者SqlSessionTemplate類型的bean。
- 12)如果你定義的是一個SqlSessionFactory類型的bean,那么最終也會被包裝為一個SqlSessionTemplate對象,并且賦值給sqlSession屬性
- 13)而在SqlSessionTemplate類中就存在一個getMapper方法,這個方法中就產(chǎn)生一個Mapper接口代理對象
- 14)到時候,當(dāng)執(zhí)行該代理對象的某個方法時,就會進入到Mybatis框架的底層執(zhí)行流程
這里注意:SpringManagedTransaction是由SqlSessionFactoryBean引入的。
分析一下Mapper代理對象執(zhí)行查詢,比如userMapper.selectById(),其底層調(diào)用的是啥?
- 其實調(diào)用的是SqlSessionTemplate.selectOne,主要是為了線程安全的,則每個線程都會獲取不同的DefaultSqlSession;如果是事務(wù),同一個線程會通過ThreadLocal存儲獲取同一個DefaultSqlSession(如果不是事務(wù),同一個線程執(zhí)行不同的sql語句也會獲取不同的DefaultSqlSession,這樣會導(dǎo)致一級緩存失效。一級緩存生效的前提是:同一個DefaultSqlSession執(zhí)行多個相同的sql語句)
- SqlSessionTemplate會調(diào)用到SqlSessionProxy.selectOne,SqlSessionProxy的InvocationHandler是SqlSessionInterceptor,在SqlSessionInterceptor#invoke()里面會getSqlSession()返回一個SqlSession(這里返回的就是DefaultSqlSession)
- SqlSessionProxy會調(diào)用至DefaultSqlSession.selectOne 線程不安全的
一級緩存失效問題:
- 一般不使用一級緩存,跟事務(wù)隔離級別會沖突。比如讀已提交想要讀取到不同結(jié)果,但是使用緩存后,讀取到的每次都一樣。
Mybatis-Spring 2.0.6版本底層源碼執(zhí)行流程
核心區(qū)別:Mybatis-Spring 1.3.2在MapperScannerRegistrar#registerBeanDefinitions就會去掃描Mapper接口并生成BD注冊到Sprign容器。而Mybatis-Spring 2.0.6在MapperScannerRegistrar#registerBeanDefinitions只是注冊了一個新的BD:MapperScannerConfigurer,掃描邏輯放到了這個類中,因為這個類是一個BeanDefinitionRegistryPostProcessor。
- 1)通過@MapperScan導(dǎo)入了MapperScannerRegistrar類
- 2)MapperScannerRegistrar類實現(xiàn)了ImportBeanDefinitionRegistrar接口,所以Spring在啟動時會調(diào)用MapperScannerRegistrar類中的registerBeanDefinitions方法
- 3)在registerBeanDefinitions方法中注冊一個MapperScannerConfigurer類型的BeanDefinition
- 4)而MapperScannerConfigurer實現(xiàn)了BeanDefinitionRegistryPostProcessor接口,所以Spring在啟動過程中時會調(diào)用它的postProcessBeanDefinitionRegistry()方法
- 5)在postProcessBeanDefinitionRegistry方法中會生成一個ClassPathMapperScanner對象,然后進行掃描
- 6)后續(xù)的邏輯和1.3.2版本一樣。
invokeBeanFactoryPostProcessors()中整合Mybatis的地方
invokeBeanFactoryPostProcessors()執(zhí)行流程:
- 1)執(zhí)行通過ApplicationContext手動添加進來的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
- 2)執(zhí)行BeanFactory中實現(xiàn)了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法。這里會將ConfigurationClassPostProcessor的BeanDefinition實例化出來,并進行調(diào)用。這里會處理MapperScannerRegistrar,是個ImportBeanDefinitionRegistrar。Mybatis-Spring 1.3.2直接在這里掃描,Mybatis-Spring 2.0.6新引入了一個BeanDefinitionRegistryPostProcessor:MapperScannerConfigurer。
- 3)執(zhí)行BeanFactory中實現(xiàn)了Ordered接口的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
- 4)執(zhí)行BeanFactory中其他的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法(這里是個循環(huán),如果新注冊了BeanDefinitionRegistryPostProcessor,會繼續(xù)循環(huán)進行處理)。Mybatis-Spring
2.0.6的MapperScannerConfigurer會在這里掃描所有Mapper接口并生成BD注冊到Spring容器。 - 5)執(zhí)行上面所有的BeanDefinitionRegistryPostProcessor的postProcessBeanFactory()方法
- 6)執(zhí)行通過ApplicationContext手動添加進來的BeanFactoryPostProcessor的postProcessBeanFactory()方法
- 7)執(zhí)行BeanFactory中實現(xiàn)了PriorityOrdered接口的BeanFactoryPostProcessor的postProcessBeanFactory()方法
- 8)執(zhí)行BeanFactory中實現(xiàn)了Ordered接口的BeanFactoryPostProcessor的postProcessBeanFactory()方法
- 9)執(zhí)行BeanFactory中其他的BeanFactoryPostProcessor的postProcessBeanFactory()方法
以上就是Spring整合Mybatis框架源碼剖析的詳細(xì)內(nèi)容,更多關(guān)于Spring整合Mybatis的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springsecurity實現(xiàn)用戶登錄認(rèn)證快速使用示例代碼(前后端分離項目)
這篇文章主要介紹了springsecurity實現(xiàn)用戶登錄認(rèn)證快速使用示例代碼(前后端分離項目),本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-03-03struts2.2.3+spring3.1.0+mybatis3.1.0框架整合集成簡單demo
本篇文章主要介紹了struts2.2.3+spring3.1.0 + mybatis3.1.0框架整合,結(jié)合在一起實現(xiàn)用戶的增刪改查功能,有需要的可以了解一下。2016-11-11springboot 使用yml配置文件自定義屬性的操作代碼
在SpringBoot中yml/yaml文件可以自定義一些屬性,以供注入給自定義bean對象的屬性,主要通過空格和層次來實現(xiàn),類似于python代碼,本文通過實例代碼給大家介紹springboot 使用yml配置文件自定義屬性,感興趣的朋友跟隨小編一起看看吧2024-03-03關(guān)于@ComponentScan?TypeFilter自定義指定掃描bean的規(guī)則
這篇文章主要介紹了關(guān)于@ComponentScan?TypeFilter自定義指定掃描bean的規(guī)則,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09mybatis-plus如何修改日志只打印SQL語句不打印查詢結(jié)果
這篇文章主要介紹了mybatis-plus如何修改日志只打印SQL語句不打印查詢結(jié)果問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06