作者 | 波哥
審校 | 孫淑娟
如果老鐵們對Spring框架足夠熟悉,整合MyBatis其實(shí)很容易理解,當(dāng)然這里假定老鐵們也已經(jīng)熟悉了MyBatis框架。
在我們正常的應(yīng)用開發(fā)過程中,使用MyBatis一般分為如下幾個步驟:
1.在配置類上增加MapperScan注解,例如:@MapperScan(basePackages = {"com.test.dao"},annotationClass = Mapper.class);
2.在basePackages指定的目錄下創(chuàng)建待MyBatis讀取的接口文件,例如:
3.在Service或者其他地方使用該Mapper來操作數(shù)據(jù)庫。
使用起來是很簡單的,但是有沒有老鐵想過,為什么做了這么一個簡單的配置,這個Mapper就能操作數(shù)據(jù)庫了?按理說這個Mapper是個接口,應(yīng)該是不能被創(chuàng)建才對??!如果你有這個疑問,證明你是個愛思考的好童鞋。
咱們直接進(jìn)入主題。Spring要與MyBatis整合,簡單來說只要解決如下兩個問題:
一、Spring如何知道哪些類應(yīng)該被管理?
要讓Spring去管理Bean的生命周期,首先需要對應(yīng)的類被Spring掃描到,并且生成DeanDefinition,然后基于BeanDefinition生成Bean。下面對Spring生成BeanDefinition的方式做個小總結(jié):
- 包含Component、Configuration、ComponentScan、Import、ImportResource注解的類;
- Import注解中指定的類、被Bean注解標(biāo)注的方法所在的類;
- 實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口,并且在registerBeanDefinitions方法中調(diào)用registry直接注冊的類;
- 實(shí)現(xiàn)了ImportSelector接口,并且在selectImports方法中返回的字符串對應(yīng)的類;
- 直接調(diào)用register方法;
- 另外Spring還提供了一個擴(kuò)展,可以讓開發(fā)者自己指定需要被管理的類對應(yīng)的類型:通過往includeFilters中添加注解類類型。
我們分析源碼,第一步得找到它的入口,Spring整合MyBatis的入口,毫無疑問是MapperScan這個注解,在MapperScan注解上包含Import(MapperScannerRegistrar.class)注解,Spring整合MyBatis正是用了Import和ImportBeanDefinitionRegistrar的方式。我們先通過一張流程圖來了解下整體流程,然后再慢慢品。
我們來看MapperScannerRegistrar這個類的繼承關(guān)系圖:
MapperScannerRegistrar是ImportBeanDefinitionRegistrar的實(shí)現(xiàn)類,Spring會去調(diào)用這個類的registerBeanDefinitions方法添加beanDefinition,這個方法中具體做了些什么呢:
獲取MapperScan注解的配置信息,比如basePackages、annotationClass,basePackages表示需要掃描的路徑,annotationClass則是指定了增加了這種注解類的類需要被Spring進(jìn)行管理,比如增加了Mapper注解的類需要被Spring管理。
生成MapperScannerConfigurer這個類型的beanDefinition,并且把MapperScan注解的配置信息添加到該beanDefinition的屬性集合中。
后續(xù)Spring就會基于這個MapperScannerConfigurer做一系列文章,看下它的繼承關(guān)系:
它是BeanDefinitionRegistryPostProcessor的實(shí)現(xiàn)類,是一個BeanFactory后置處理器,Spring會調(diào)用該類的postProcessBeanDefinitionRegistry方法來添加beanDefinition的操作,MapperScannerConfigurer這個類中具體實(shí)現(xiàn)如下:
它定義了ClassPathMapperScanner這個掃描器,然后使用這個掃描器來掃描類,掃描哪些類呢?掃描有Mapper注解的類,看它的關(guān)系知道,它是ClassPathBeanDefinitionScanner的子類,而spring則是使用ClassPathBeanDefinitionScanner來進(jìn)行掃描的。
為什么ClassPathMapperScanner能夠掃描到帶有Mapper注解的類呢?看上面代碼,就是通過調(diào)用registerFilters方法來添加includeFilter(實(shí)際類型是:TypeFilter),這個就是Spring提供的擴(kuò)展點(diǎn),讓咱們自己來指定需要被掃描的類,這里使用的是MappScan注解中annotationClass屬性配置的注解類型,我們這里配置了Mapper,所以調(diào)用scan方法開啟掃描后,Spring就會將包含Mapper注解的類掃描為BeanDefinition。注意這里的掃描能力還是調(diào)用Spring的掃描器來實(shí)現(xiàn)的,ClassPathMapperScanner并沒有修改,只是當(dāng)掃描完成后,ClassPathMapperScanner會對掃描出的BeanDefinition進(jìn)行重新處理,主要是把原來的BeanClass修改成了MapperFactoryBean.class:
而這個MapperFactoryBean是FactoryBean的實(shí)現(xiàn)類,老鐵們,F(xiàn)actoryBean這種Bean有什么特點(diǎn)?這個可是面試的高發(fā)點(diǎn)哦。
做個小小的總結(jié):Spring掃描到有Mapper注解的類,生成BeanDefinition,并且將這一類BeanDefinition的BeanClass的值修改為MapperFactoryBean,也就是說它的類型不再是咱們自己編寫的Mapper接口了,而是一個FactoryBean,這樣Spring就能做妖了。
二、Mapper注解的類是接口
那如何實(shí)例化呢?
到這一步,其實(shí)老鐵們也大概清楚了,Spring在實(shí)例化Mapper實(shí)例時,實(shí)際上首先會實(shí)例化MapperFactoryBean,然后再調(diào)用它的getObject方法。我們知道在Java里面接口是肯定不能被實(shí)例化的,那這個被實(shí)例化的對象只能是一個代理對象,所以我們有理由猜想這個getObject方法應(yīng)該是用來創(chuàng)建代理對象的。要創(chuàng)建代理對象,得從以下兩個方面著手:
1.準(zhǔn)備工作
這里Spring準(zhǔn)備的是接口類型和創(chuàng)建代理對象的代理工廠。具體如何準(zhǔn)備的呢?來看上述MapperFactoryBean類型的整體繼承關(guān)系:
它實(shí)現(xiàn)了InitializingBean,于是可以知道,在MapperFactoryBean初始化完成后,Spring會調(diào)用它的afterPropertiesSet方法,從而會執(zhí)行到checkDaoConfig方法:
在該方法中調(diào)用configuration的addMapper方法,這個方法里面到底做了啥?
看出門道了嗎?其實(shí)就是使用Mapper的接口類型作為key,MapperProxyFactory做為value,然后添加到mapperRegistry對象的Map集合中,注意這個type同時也是MapperProxyFactory對象的構(gòu)造參數(shù)哦。
2.實(shí)例化
上述動作已經(jīng)準(zhǔn)備好了,接下來就應(yīng)該是創(chuàng)建了。Spring在創(chuàng)建完成MapperFactoryBean對象后,最終會調(diào)用它的getObject方法來獲得真實(shí)的對象:
getObject方法中,會調(diào)用getMapper方法,該方法中從knowMappers這個Map集合中拿到MapperProxyFactory對象,這個對象不就是我們在準(zhǔn)備階段添加的嘛!它就是用來創(chuàng)建代理對象的工廠。
從上面代碼中也不難看出,確實(shí)是為咱們自己的接口創(chuàng)建了代理對象,而代理類的處理類則是MapperProxy對象,也就是說對所有接口對象的調(diào)用,都會進(jìn)入MapperProxy的Invoke方法,至此Spring成功對接MyBatis。
作者介紹
波哥,互聯(lián)行業(yè)從業(yè)10余年,先后擔(dān)任項(xiàng)目總監(jiān)及架構(gòu)師。目前專攻技術(shù),喜歡研究技術(shù)原理。技術(shù)全面,主攻java,精通JVM底層機(jī)制及Spring全家桶底層框架原理,熟練掌握當(dāng)前主流的中間件、服務(wù)網(wǎng)格等技術(shù)原理。