Mybatis源碼(一)之Spring整合Mybatis掃描dao

帶着幾個問題我們來分析下Spring是如何整合Mybatis的

1.Spring是如何掃描Dao,將其轉換成BeanDefinition註冊到BeanFactory的?
2.爲什麼我們可以直接使用dao接口調用方法就能操作數據庫了?
3. 原生Mybatis的使用和jdbc的使用與跟Spring整合之後,我們到底哪裏方便使用了

你可能猜到:
問題一使用Spring的拓展接口。
問題二使用的是代理。
問題三使用模板解決重複代碼問題。

項目中大多會使用Spring和Mybatis的,通常會包含如下幾個步驟
引入jar
spring.version.jar
mybatis-spring-1.2.3.jar整合
mybatis-3.3.0.jar
配置

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.wuage.hrd.dao" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<bean id="sqlSessionFactory"    class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="mapperLocations">
            <list>
                <value>classpath:sqlmap/*Mapper.xml</value>
            </list>
        </property>
        <property name="dataSource" ref="dataSource" />
</bean>

由此便知核心類MapperScannerConfigurer
從配置中就可以看出改類的兩個功能:
A)設置掃描那些dao的路徑
B)指定SqlSessionFactory

MapperScannerConfigure類圖

實現BeanDefinitionRegistryPostProcessor 而該類有繼承了BeanFactoryPostProcessor 即Spring的拓展點之一在實例化之前可以做一些事情,比如這裏的掃描dao的描述對象BeanDefinition註冊到工廠中,剩下的活就交由Spring來創建Dao對象了。 而然很遺憾BeanFactoryPostProcessor在此只是空實現,完成掃描功能還是使用的BeanDefinitionRegistryPostProcessor的方法實現掃描功能!

BeanDifinitionRegistryPostProcessor拓展點完成dao掃描功能

整個類的功能:
1、掃描dao接口的過程就是BeanDefinition註冊到DefaultListableBeanFactory中,同時也將別名註冊進去了。
2、將所有beanDefinition的真實實現改成是MapperFactoryBean
beanDefinition的屬性mapperInterface就是dao接口設置上sqlSessionFactory

調用棧如下所示:
掃描dao註冊beanDefinition調用棧

這裏的this.mapperFactory就是MapperFactoryBean
至此問題一已經結束!!!!(doScanner方法內部調用的processBeanDefinition這一步有圖的方法棧中可以看出,有些同學沒銜接上!!這裏補充下)

將beanDefinition的實現BeanClass改成MapperFactoryBean 這是偷天換日了。
看下這個MapperFactoryBean是什麼神聖!

MapperFactoryBean

FactoryBean與BeanFactory
1)BeanFactory,以Factory結尾,表示它是一個工廠類(接口),用於管理Bean的一個工廠。在Spring中,BeanFactory是IOC容器的核心接口,它的職責包括:實例化、定位、配置應用程序中的對象及建立這些對象間的依賴。
2)以Bean結尾,表示它是一個Bean,不同於普通Bean的是:它是實現了FactoryBean接口的Bean,根據該Bean的ID從BeanFactory中獲取的實際上是FactoryBean的getObject()返回的對象,而不是FactoryBean本身,如果要獲取FactoryBean對象,請在id前面加一個&符號來獲取。—摘抄自網上帖子
Spring在BeanFactory創建bean的時候會調用FactoryBean的getObject();

父類SqlSessionDaoSupport.getSqlSession();
我們將MapperFactoryBean.getSqlSession().getMapper()分成兩步看:
1)創建SqlSession–實際生成的是代理
2)創建dao接口的.getMapper(intf);–實際也是代理

MapperFactoryBean.getObject()方法

SqlSession的實現類(Spring環境下會多處一個實現類SqlSessionTemplate):
sqlSession實現類

既然是整合定會用SqlSessionTemplate(Spring整合mybatis的部分),整合完之後再使用原生SqlSession的實現,我們繼續看是否是這個套路!!

創建SqlSession–SqlSessionFactory 同時給其成員SqlSession賦值代理類

SqlSessionDaoSupport.getSqlSession()方法

SqlSessionFactory是我們在配置文件中配置的bean,看來這裏的sqlSession就是SqlSessionTemplate
再看SqlSessionTemplate的構造方法是如何初始化SqlSession的其實是生成了SqlSession的代理類了。newProxyInstance是靜態導入的所以沒有Proxy.newProxyInstance(),看代碼的時候注意下不要蒙圈。

SqlSessionDaoSupport.getSqlSession()

看到SqlSession是代理類,這裏的jdk動態代理InvocationHandler實現 SqlSessionInterceptor類比較重要下片單獨討論。

創建MapperProxy
我們使用的dao都是MapperProxy
代理sqlSession的實現類之一 sqlSessionTemplate.getMapper(interf)的調用棧如下所示:

MapperProxy

Configure.getMapper(interface,sqlSessionTemplate)
MapperProxyFactory proxyFactory = MapperRegistry.getMapper(interface)
proxyFactory.newInstance(sqlSessionTemplate,interface)

dao的代理類MapperProxy就這樣實現
問題二也就結束了

同樣也是MapperProxy也是jdk動態代理InvocationHandler的實現,也留到下片分析。

總結:

上面是Spring啓動過程的分析,setSessionFactory被調用,創建一個SqlSessionTemplate,sqlSessionTemplate本身也是SqlSession,同時引用了一個SqlSession代理類sqlSessionInterceptor.

MapperScannerConfigure掃面每個dao,並設置的beanDefinition的beanClass都是MapperFactoryBean會被Spring調用getObject方法

從SqlSessionTemplate.getMapper()–>Configuration.getMapper(daoInterf,SqlSessionTemplate),
–>mapperRegistory.getMapper(daoInterface,sqlSessiontemplate),

爲每個Dao都new MapperProxy(sqlSessionTemplate,interface)
dao生成一個MapperProxy代理類。

留下兩個代理類到下篇博文在講解:
SqlSessionInterceptor
MapperProxy

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章