????????讀完本文后你的收獲:
? ? ????????????1速挑、了解Mybatis和Spring整合的底層原理
? ? ? ? ????????2、知道為什么我們在spring中定義了接口就可以直接操作數(shù)據(jù)庫
? ? ? ????????? 3副硅、了解Spring中的拓展點和FactoryBean的使用
? ? ? ? 首先來分析要解決的問題姥宝,如下圖所示恐疲,我們沒有在Admapper上加@Component等注解腊满,我們?yōu)槭裁纯梢詮腷ean工廠中獲取AdMapper的實例培己,并且可以通過@Resource注入對象;
? ? ? ? 首先來解決第一個問題碳蛋,沒有加注解是怎么加入到容器中的。
? ? ? ? 在mybatis和spring整合時省咨,一般會在配置類上加@MapperScan,我們點進(jìn)去這個注解發(fā)現(xiàn)茸炒,它@Import(MapperScannerRegistrar.class)了另一個類愕乎。MapperScannerRegistrar這個類呢做的就是把所有的mapper變成BeanDefinition壁公,從而后邊可以被創(chuàng)建感论。
? ? ? ? 通過繼承關(guān)系紊册,我們發(fā)現(xiàn)這個類實現(xiàn)了ImportBeanDefinitionRegistrar這個接口比肄,這個接口是spring當(dāng)中的一個擴(kuò)展點,基于接口中的registerBeanDefinitions方法我們可以做到向bean工廠中注入BeanDefinition囊陡。
? ? ? ? 看一下這個方發(fā)的實現(xiàn),主要是doScan方法撞反,首先調(diào)用父類的doScan掃描我們配置的包路徑妥色,獲得所有的class遏片,包裝成BeanDefinition嘹害,假如說我們這些接口都有實現(xiàn)類撮竿,那么這時我們把這個BD直接放入到BDMap中笔呀,spring就會幫助我們創(chuàng)建對象幢踏,這就解釋了第一個疑問,這些接口的定義信息是怎么放進(jìn)去的房蝉。
? ? ? ? 我們雖然拿到了BD并且把它放到了BDMap中去,如果spring直接使用當(dāng)前的bd是沒辦法創(chuàng)建成功對象的微渠,因為我們只寫了接口搭幻,這時我們就要改變BeanDefinition的屬性敛助,來讓bean工廠可以創(chuàng)建對象粗卜。
? ??????processBeanDefinitions方法就是做的這個事纳击,我們看到這個方法的接口參數(shù)是Set<BeanDefinitionHolder> beanDefinitions续扔,也就是上一步掃描的結(jié)果,循環(huán)修改BD的信息
? ? ? ? 主要解析以下三行代碼
? ? ? ?代碼1: definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
? ? ? ?代碼2:?definition.setBeanClass(this.mapperFactoryBean.getClass());
? ?????代碼3:?definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
? ? ? ? 解析代碼直接焕数,因為 它設(shè)置了一個mapperFactoryBean,所以需要知道m(xù)apperFactoryBean的結(jié)構(gòu)
? ? ? ? 可以看到MapperFactoryBean實現(xiàn)了繼承了 SqlSessionDaoSupport 實現(xiàn)了FactoryBean堡赔;FactoryBean這個接口大家都不陌生识脆,可以通過getObject方法獲得我們想要的對象善已,實時上也是這么做的灼捂。
? ? ? ? ? ? 代碼一? ?definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());此處相當(dāng)于我們直接調(diào)用setMapperInterface方法,只不過是spring會把字符串轉(zhuǎn)換成class對象悉稠,為什么要設(shè)置class類型,因為我們的mapper通常有好多接口艘包,而且我們也不知道具體的名字的猛,需要程序動態(tài)的注入進(jìn)來想虎;? ? ?
? ??????????代碼2:?definition.setBeanClass(this.mapperFactoryBean.getClass());設(shè)置bd的beanClass類型卦尊,bd在創(chuàng)建的時候會根據(jù)這個類型去創(chuàng)建對象舌厨,因為mapperFactoryBean是個對象岂却,所以可以被創(chuàng)建。
? ??????????代碼3:?definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); 修改默認(rèn)的裝配類型為byType,這樣我們就可以通過@Autowired來自動裝配
? ? ? ? ? ? 到現(xiàn)在實際上已經(jīng)把bd的定義修改完了煌恢,然后在創(chuàng)建對象時回調(diào)用getObject方法生成代理對象骇陈,并放到容器中震庭。所以容器中放的是接口的代理對象。那么我們也可以直接拿出來使用
? ? ? ? ? ? 上邊已經(jīng)對文章開頭處的疑問做了闡述器联,希望能對大家有所幫助,其實還有很多細(xì)節(jié)比如為什么要設(shè)置AutowireMode婿崭,不設(shè)置會怎么樣,大家可以查看一下源碼去解決這個問題氓栈,并且會很有收獲發(fā)現(xiàn)spring默認(rèn)的裝配類型不是按類型裝配渣磷。
? ? ? ? ? ? 再次總結(jié)一下文章中所用的知識點
????????????ImportBeanDefinitionRegistrar:這個接口可以幫助我們動態(tài)的向bean工廠中添加bd或者修改bd
????????????FactoryBean:這個接口可以讓我們通過getObject方法獲取對象并且執(zhí)行是不是單例? ? ? ? ? ??
? ??????????AutowireMode:裝配類型,可以實現(xiàn)自動裝配