1. Spring Boot 自动配置 Mybatis

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的主要属性有:

  1. MybatisProperties:yaml文件中的属性配置
  2. Interceptor[] :拦截器数组
  3. TypeHandler[] :类型解析器等

构造方法:

  1. 从 yaml 文件中获取 mybatis 前缀的属性封装成对象MybatisProperties,赋值给 properties 。
  2. 获取拦截器
  3. 获取typeHandlers
  4. 其他。。。
创建 sqlSessionFactory

前提:

  1. 依赖DataSource 数据源 @ConditionalOnSingleCandidate(DataSource.class)。要求 BeanFactory 中存在指定类并且可以确定单个候选项才会匹配成功
  2. 在数据源完成自动配置之后配置 sqlSessionFactory = new SqlSessionFactoryBean().getObject();

正文:

  1. 创建一个工厂bean:​MybatisSqlSessionFactoryBean​​

  2. 工厂 bean 设置数据源dataSource。依赖注入,此时容器中已经有了dataSource,直接拿来用。

  3. 初始化配置属性

    1. 如果有配置 mybatis-config.xml 则设置configLocation 属性。
    2. 应用yaml 文件中嵌套配置 configuration到 factoryBean。
    3. TypeHandler
    4. 拦截器
  4. 通过工厂Bean的getObject方法获取sqlSessionFactory。

    1. 如果配置项 mapperLocation不为空,循环解析每一个 mapper.xml文件

    2. XMLMapperBuilder.parse() 解析xml 文件

      1. 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>
        1. namespace判空。namespace 为 mapper 接口类的 String 字符串
        2. 解析 parameterMap 和 resultMap节点
        3. 解析 sql节点
        4. 解析 select|insert|update|delete
        5. 将该文件添加到已解析集合中。protected final Set<String> loadedResources;
        6. 将 mapper 类添加到 mapper注册表中,解析 mapper 类上的注解@Select、@SelectProvider、@ResultSet 等。
        7. 解析方法上的 SQL语句parseStatement。
创建sqlSessionTemplate
  1. 使用 jdk 动态代理,创建SqlSessionProxy,增强逻辑为SqlSessionInterceptor​。代理了SqlSession的创建和获取、事务、关闭。即在调用SqlSession接口中的每一个方法时,都会调用SqlSessionInteceptor的invoke逻辑。

  2. SqlSessionInterceptor​的逻辑为:

    1. 创建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。
      java 复制代码
      SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, 
      	SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
      1. 如果 session 为 null,

        1. 利用事务工厂创建一个事务,
        2. 新建一个 Executor:这个是干嘛的,不太清楚哇。
        3. 新建一个 DefaultSqlSession。
        4. 创建sqlSessionHolder 将 sqlSession 包起来。
        5. 将sqlSessionHolder存放到TransactionSynchronizationManagersynchronizations中。synchronizations是一个 set 集合。
    2. 调用业务方法

    3. 如果有事务,提交事务

    4. 关闭 session。关闭前会释放 holder。​holder.released();

      java 复制代码
      SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
创建MapperScannerRegistrarNotFoundConfiguration

主要在于根据配置创建MapperScannerConfigurer​,

@Import({AutoConfiguredMapperScannerRegistrar.class}) 这个AutoConfiguredMapperScannerRegistrar类非常关键,因为他在扫描@Mapper 注解

  1. BeanDefinitionBuilder:创建MapperScannerConfigurer的 BeanDefinitionBuilder
  2. 设置扫描注解类:Mapper.class
  3. 设置扫描包路径
  4. 设置sqlSessionFactoryBeaName
  5. 设置 sqlSessionTemplateBeanName
  6. 注册MapperScannerConfigurer到注册表中
MapperScannerConfigurer

实现了 BeanDefinitionRegistryPostProcessor ,工作在bean 定义的注册阶段。

扫描@Mapper 注解,为什么需要单独处理呢?因为 Mapper 注解是 mybatis 自定义注解,和 Component 注解没有关系。这个类会将@Mapper 注解下的类也实例化后放入到 Spring 容器中。

相关推荐
郑祎亦26 分钟前
Spring Boot 项目 myblog 整理
spring boot·后端·java-ee·maven·mybatis
jokerest12314 小时前
web——sqliabs靶场——第十三关——报错注入+布尔盲注
mybatis
武子康15 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
WindFutrue17 小时前
使用Mybatis向Mysql中的插入Point类型的数据全方位解析
数据库·mysql·mybatis
AiFlutter18 小时前
Java实现简单的搜索引擎
java·搜索引擎·mybatis
天天扭码21 小时前
五天SpringCloud计划——DAY1之mybatis-plus的使用
java·spring cloud·mybatis
武子康2 天前
Java-05 深入浅出 MyBatis - 配置深入 动态 SQL 参数、循环、片段
java·sql·设计模式·架构·mybatis·代理模式
2的n次方_2 天前
MyBatis——#{} 和 ${} 的区别和动态 SQL
数据库·sql·mybatis
jokerest1232 天前
web——sqliabs靶场——第十二关——(基于错误的双引号 POST 型字符型变形的注入)
数据库·sql·mybatis