mybatis源码分析—— 与spring框架的整合
mybatis-spring.jar 是mybatis与spring的整合,使用mybatis可以完成访问数据库的操作,通过SqlSession可以拿到Mapper的对象,
SqlSession维护了Configuration对象,缓存了knowMappers,class——MapperProxyFactory的映射,所以如果单纯的使用mybatis可以直接
sqlSession.getMapper手动获取。
一:单纯使用mybatis的方法
1:注册,sqlSessionFactoryBuildler或者SqlSessionFactoryBean创建SqlSessionFactory的时候,会解析mybatis-config.xml,最后一步
会解析mapper.xml文件,其中解析命名空间的时候,会将类型与动态代理工厂绑定。
数据源配置:
##数据源配置 jdbc.driverClass=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/study jdbc.username=root jdbc.password=root
mybatis-config.xml中的配置数据源以及mapper.xml文件
<environments default="development"> <!-- 配置环境,id:环境的唯一标识 --> <environment id="development"> <!-- 事务管理器,type:使用jdbc的事务管理器 --> <transactionManager type="JDBC" /> <!-- 数据源,type:池类型的数据源 --> <dataSource type="POOLED"> <!-- 配置连接信息 --> <property name="driver" value="${jdbc.driverClass}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments> <!-- 配置映射文件:用来配置sql语句和结果集类型等 --> <mappers> <mapper resource="UserMapper.xml" /> </mappers>
解析命名空间并注册绑定:
2:使用的时候直接通过SqlSession.getMapper(class)获取Mapper对象(代理对象)
测试代码:
可以正常查询结果:
二:与spring的整合是怎么实现的
在spring-context.xml的文件中加载配置,配置数据源bean,使用SqlSessionFactoryBean创建SqlSessionFactroy
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:application.properties" /> <!-- 配置数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClass}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <!-- 指定数据源和配置文件路径 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <!-- 自动扫描mapping.xml文件 --> <property name="mapperLocations" value="classpath:UserMapper.xml"></property> </bean> <!-- 配置单个mapper接口的映射器 --> <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.example.mybatis.mapper.UserMapper"></property> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean> <!--<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.example.mybatis.mapper"></property> </bean>--> </beans>
这里创建SqlSessionFactory对象的过程与mybatis直接创建有点区别,mybatis是通过SqlSessionFactoryBuilder创建,这里是FactoryBean
这个类实现了FactoryBean接口,InitializingBean接口
重写afterPropertiesSet,在这里解析mybatis-config.xml文件,生成Configuration对象,封装到SqlSessionFactory中返回。
解析mapper.xml文件,解析过程可以参考前面章节的mybatis-config.xml的解析。
上面是解析mapper.xml配置文件,将解析后的信息封装到configuration中,下面看一下如何整合绑定的
mybatis-spring中有个比较重要的类MapperFactoryBean,实例化userMapper对象(代理),是通过创建MapperFactoryBean
这个bean元素下面有两个子元素,一个是mapperInterface,代理的接口,一个是sqlSessionFactory,在创建Bean对象的时候会设置属性
设置sqlSessionFactory的时候实际上是把sqlSessionFactory封装到了sqlSessionTemplate中
这个类实现了FactoryBean接口,所以实际上getBean得到的对象是getObject返回的代理对象
这里的sqlSession就是上面再解析自定义标签bean的时候,实例化userMapper时,设置SqlSessionFactory的时候创建的
后面的过程就是mybatis中的逻辑,根据类type获取Mapper代理工厂,然后newInstance创建代理对象。
来看一下创建sqlSessionTemplate的过程,所有的增删改查的动作都会为他给sqlSessionProxy代理来完成
代理增强:
每次执行增删改查操作都会创建一个SqlSession会话
这是单个mapper和spring的整合,如果mapper比较多,使用这种配置的方式就比较繁琐了
所以扫描的方式相比效率就搞多了,下面我们来看一下扫描的逻辑:
配置如下:
把上面配置的单个的mapper注释掉,配置通过路径扫描来创建mapper代理:
首先来分析一下这个类:
实现了四个接口,我们来复习一下这四个接口的作用:
BeanDefinitionRegistryPostProcessor,这个接口是在spring标准的BeanDefinition注册完成后,可以自定义BeanDefinition完成注册或者修改BeanDefinition的信息,也可以直接注册对象,是在BeanDefinition注册时期
InitializingBean接口,是bean实例化完成后的初始化方法,是在spring的aware接口初始化之后调用的
ApplicationContextAware接口:是在bean实例化完成后,初始化完成BeanNameAware等3个之后,使用BeanPostProcessor完成的初始化
BeanNameAware接口:是在bean实例化后,初始化的时候调用的
初始化顺序:BeanNameAware 早于 ApplicationContextAware 早于 InitializingBean
扫描并注册BeanDefinition对象,然后修改BeanDefinition对象的属性
具体扫描的逻辑:
具体设置属性信息,就是将扫描并注册的BeanDefinition,修改属性BeanClass 为MapperFactoryBean
这样扫描的BeanDefinition对象 ,实例化的时候都是MapperFactoryBean的实例了,不需要每个mapper都去配置xml了。