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

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