1. Spring Boot 自动配置 Mybatis
自动配置
约定大于配置,缺省的配置看这个注解 @EnableConfigurationProperties({MybatisProperties.class})。这个注解中引入了MybatisProperties类,包含了一些默认的配置。
java
@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
}
MybatisAutoConfiguration的主要属性有:
- MybatisProperties:yaml文件中的属性配置
- Interceptor[] :拦截器数组
- TypeHandler[] :类型解析器等
构造方法:
- 从 yaml 文件中获取 mybatis 前缀的属性封装成对象
MybatisProperties
,赋值给 properties 。 - 获取拦截器
- 获取typeHandlers
- 其他。。。
创建 sqlSessionFactory
前提:
- 依赖DataSource 数据源 @ConditionalOnSingleCandidate(DataSource.class)。要求 BeanFactory 中存在指定类并且可以确定单个候选项才会匹配成功
- 在数据源完成自动配置之后配置 sqlSessionFactory = new SqlSessionFactoryBean().getObject();
正文:
-
创建一个工厂bean:
MybatisSqlSessionFactoryBean
-
工厂 bean 设置数据源dataSource。依赖注入,此时容器中已经有了dataSource,直接拿来用。
-
初始化配置属性
- 如果有配置 mybatis-config.xml 则设置configLocation 属性。
- 应用yaml 文件中嵌套配置 configuration到 factoryBean。
- TypeHandler
- 拦截器
-
通过工厂Bean的getObject方法获取sqlSessionFactory。
-
如果配置项 mapperLocation不为空,循环解析每一个 mapper.xml文件
-
XMLMapperBuilder.parse() 解析xml 文件
-
configurationElement
解析mapper 节点java<mapper namespace="com.pansnow.changcheng.mapper.UserMapper"> <select id="queryUserList" resultType="com.pansnow.changcheng.pojo.User"> select * from user </select> <insert id="addUser" parameterType="com.pansnow.changcheng.pojo.User"> insert into user(id, name, pwd) values (#{id}, #{name}, #{pwd}); </insert> <update id="updateUser" parameterType="com.pansnow.changcheng.pojo.User"> update user set name=#{name}, pwd=#{pwd} where id = #{id}; </update> <delete id="deleteUser" parameterType="int"> delete from user where id = #{id}; </delete> </mapper>
- namespace判空。namespace 为 mapper 接口类的 String 字符串
- 解析 parameterMap 和 resultMap节点
- 解析 sql节点
- 解析 select|insert|update|delete
- 将该文件添加到已解析集合中。
protected final Set<String> loadedResources;
- 将 mapper 类添加到 mapper注册表中,解析 mapper 类上的注解@Select、@SelectProvider、@ResultSet 等。
- 解析方法上的 SQL语句parseStatement。
-
-
创建sqlSessionTemplate
-
使用 jdk 动态代理,创建SqlSessionProxy,增强逻辑为
SqlSessionInterceptor
。代理了SqlSession的创建和获取、事务、关闭。即在调用SqlSession接口中的每一个方法时,都会调用SqlSessionInteceptor的invoke逻辑。 -
SqlSessionInterceptor
的逻辑为:-
创建SqlSession。使用 selSessionFactory
代理对象是如何获取defaultSqlSession ,在代理方法中通过SqlSessionUtils 的方法获取SqlSession
- 它会首先获取SqlSessionHolder,SqlSessionHolder用于在TransactionSynchronizationManager中保持当前的SqlSession。
- 如果holder不为空,并且holder被事务锁定,则可以通过holder.getSqlSession()方法,从当前事务中获取sqlSession,即 Fetched SqlSession from current transaction。
- 如果不存在holder或没有被事务锁定,则会创建新的sqlSession,即 Creating a new SqlSession,通过sessionFactory.openSession()方法。
- 如果当前线程的事务是活跃的,将会为SqlSession注册事务同步,即 Registering transaction synchronization for SqlSession。
javaSqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
-
如果 session 为 null,
- 利用事务工厂创建一个事务,
- 新建一个 Executor:这个是干嘛的,不太清楚哇。
- 新建一个 DefaultSqlSession。
- 创建sqlSessionHolder 将 sqlSession 包起来。
- 将sqlSessionHolder存放到
TransactionSynchronizationManager
的synchronizations
中。synchronizations
是一个 set 集合。
-
调用业务方法
-
如果有事务,提交事务
-
关闭 session。关闭前会释放 holder。
holder.released();
javaSqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
-
创建MapperScannerRegistrarNotFoundConfiguration
主要在于根据配置创建MapperScannerConfigurer
,
@Import({AutoConfiguredMapperScannerRegistrar.class}) 这个AutoConfiguredMapperScannerRegistrar类非常关键,因为他在扫描@Mapper 注解
BeanDefinitionBuilder
:创建MapperScannerConfigurer
的 BeanDefinitionBuilder- 设置扫描注解类:Mapper.class
- 设置扫描包路径
- 设置sqlSessionFactoryBeaName
- 设置 sqlSessionTemplateBeanName
- 注册
MapperScannerConfigurer
到注册表中
MapperScannerConfigurer
实现了 BeanDefinitionRegistryPostProcessor ,工作在bean 定义的注册阶段。
扫描@Mapper 注解,为什么需要单独处理呢?因为 Mapper 注解是 mybatis 自定义注解,和 Component 注解没有关系。这个类会将@Mapper 注解下的类也实例化后放入到 Spring 容器中。