一、初始化SqlSessionFactory
核心流程
核心使用到了SqlSessionFactoryBean的afterPropertiesSet、getObject方法
afterPropertiesSet:用于初始化并封装数据
getObject:用于注入DefaultSqlSessionFactory对象到容器中
详情逻辑
一、在将SqlSessionFactoryBean放在IOC容器过程中,由于SqlSessionFactoryBean实现了InitializingBean#afterPropertiesSet方法
afterPropertiesSet会做以下几件事
1. 将配置信息封装到Configuration对象中
2. 读取并解析各个配置的xml文件并封装到Configuration的Map<String, MappedStatement> mappedStatements对象中,key是xml的名称空间+各个标签id,value是标签的内容
3. 同时会加载各个名称空间得到Class对象,构建相应的代理工厂对象MapperProxyFactory到MapperRegistry的Map<Class<?>, MapperProxyFactory<?>> knownMappers中,方便创建代理对象使用
将封装好数据的Configuration对象作为属性放在创建的DefaultSqlSessionFactory对象中,这样就构建了DefaultSqlSessionFactory对象二、SqlSessionFactoryBean其实现了FactoryBean#getObject方法,getObject方法会将上边的DefaultSqlSessionFactory对象注入到IOC容器中
相应配置
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><!--映射配置文件位置--><property name="mapperLocations" value="classpath:/mapper/*.xml"/><!--其他配置--><property name="configuration"><bean class="org.apache.ibatis.session.Configuration"><!--开启列名转驼峰映射配置--><property name="mapUnderscoreToCamelCase" value="true"/><!--日志输出到控制台--><property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/></bean></property>
</bean>
二、创建接口的代理对象并放在IOC容器中
核心流程
为每个接口创建间桥梁sqlSessionTemplate对象
将创建的映射器接口代理对象放在IOC容器中
详情逻辑
MapperFactoryBean继承了SqlSessionDaoSupport,会为每个接口创建sqlSessionTemplate对象,其作为一个中间桥梁,当sqlSessionTemplate执行方法时会委托给其内部的sqlSessionProxy代理对象执行
sqlSessionProxy代理对象执行相应的方法时会走增强逻辑SqlSessionInteceptior#invoke方法,invoke方法才会真正创建DefaultSqlSession对象,
创建DefaultSqlSession对象有以下重要步骤
- 会将DefaultSqlSession作为value,DefaultSqlSessionFactory对象作为key放在线程的局部变量TheadLocal
- 会与Spring的事务进行挂钩,创建Executor对象(比如SimpleExecutor),并将Spring事务放在BaseExecutor,为了后续在Executor执行方法时从Spring事务中获取数据库连接(spring事务本质上也是通过ThreadLocal对象拿的数据库连接,数据源作为key,数据库连接对象作为value)
相关方法:
org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke
org.mybatis.spring.SqlSessionUtils#getSqlSession()
org.apache.ibatis.executor.SimpleExecutor#prepareStatement
org.mybatis.spring.transaction.SpringManagedTransaction#openConnection
通过org.mybatis.spring.mapper.MapperFactoryBean#getObject方法将生成的映射器接口代理对象放在IOC容器中
<bean id="tbRoleInfoMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"><property name="mapperInterface" value="com.abucloud.mapper.TbRoleInfoMapper"/><property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<bean id="userInfoMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"><property name="mapperInterface" value="com.abucloud.mapper.UserInfoMapper"/><property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
补充:MapperFactoryBean有两个重要作用,一是将接口的代理对象注入到容器中,二是当没有指定mapperLocations配置文件时,会自动以接口的全限定名去相同的类路径下找匹配的xml文件
三:代理对象执行目标方法
核心流程
通过MapperProxy#invoke作为入口,SqlSesionTemplate中间桥梁委托给sqlSessionProxy执行,再通过SqlSessionInterceptor#invoke方法创建真正的DefaultSqlSession对象执行crud
详情逻辑
1. 代理对象执行目标方法,就会执行MapperProxy#invoke方法,会构建MapperMethod,解析拿到映射器接口及相应的mapperStatement对象
2. 通过mapperMethod找到执行哪个操作(crud),接着继续委托给sqlSessionTemplate(是在创建MapperProxy对象塞进去的)执行
3. sqlSessionTemplate会继续委托给sqlSessionProxy对象执行,其是代理对象,执行操作会触发增强逻辑SqlSessionInterceptor执行
4. SqlSessionInterceptor内部会创建DefaultSqlSession对象,反射执行内部相应的方法
5. DefaultSqlSession会继续委托给相应的Exexutor(比如是simpleExecutor)
6. Exexutor会从数据源中拿数据库连接对象,通过ParameterHandler进行参数解析(Java数据->sql数据)
7. 最终通过PreparedStatementHandler执行jdbc操作,拿到返回结果集通过ResultSetHandler处理解析结果集(sql数据->Java数据)
其他
1.在同一个spring事务中,自始至终使用的都是同一个DefalutSqlSession对象,原因是创建DefalutSqlSession时会将其绑定到线程的局部变量中,key是DefalutSqlSessionFactory,value是DefalutSqlSession对象
2.真正关闭sqlSession对象在SqlSessionTemplate的beforeCompletion/afterCompletion,而这两个方法beforeCompletion/afterCompletion触发的时机是spring事务TransationAspectSupport执行结束后会触发3.TransactionSynchronizationManager#isSynchronizationActive的理解