1. Spring Boot 自动配置 Mybatis 流程

1. Spring Boot 自动配置 Mybatis

自动配置过程中做了3个主要bean的创建及很重要的一些事情。

  1. sqlSessionFactory、sqlSessionTemplate、MapperScannerConfigurer 等配置bean的创建。
  2. sqlSessionFactory:解析 xml配置文件,并将MappedStatement放入到HashMap中。
  3. sqlSessionTemplate:代理了 DefaultSqlSession,增强了Session的创建获取、关闭、事务等能力。
  4. MapperScannerConfigurer:扫描@Mapper注解,并生成这些类的BeanDefinition放入到注册表中。
自动配置

约定大于配置,缺省的配置看这个注解 @EnableConfigurationProperties({MybatisProperties.class})。这个注解中引入了MybatisProperties类,包含了一些默认的配置。

@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. 对关键配置做断言处理:dataSource​、sqlSessionFactoryBuilder​、configuration​、​configLocation

    2. buildSqlSessionFactory​:开始构建sqlSessionFactory。

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

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

        1. 如果没有解析过该 xml 文件,则进行解析,如果解析过,直接跳过。

        2. configurationElement​​​​​解析mapper 节点

          <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>
          
        3. namespace判空。namespace 为 mapper 接口类的 String 字符串

        4. 解析 parameterMap 和 resultMap节点

        5. 解析 sql节点

        6. 解析 select|insert|update|delete

      3. 将该文件添加到已解析集合中。

        protected final Set<String> loadedResources;
        if (!this.configuration.isResourceLoaded(this.resource)) {
                 
         ...
              this.configuration.addLoadedResource(this.resource);
         ...
        }
        
      4. 将 mapper 类添加到 mapper注册表中。

        private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
        
        this.knownMappers.put(type, new MapperProxyFactory(type));
        
        1. knownMapper是一个mapper接口类和他的代理工厂的一个映射。

        2. 解析 mapper 类上的注解@Select、@SelectProvider、@ResultMap 等注解,用于解析 mapper 类中方法上的 SQL 语句。

        3. 解析方法上的xml文件中的 SQL语句 parseStatement。

          1. 具体的解析过程就不谈了,太复杂,先不看了。
          2. MybatisSqlSessionFactoryBean​​中有一个属性mappedStatements​​。这是一个 HashMap
          3. 将解析过后的MappedStatement​​添加到 map 中
创建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。
      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存放到TransactionSynchronizationManager​的synchronizations​中。synchronizations​是一个 set 集合。
    2. 调用业务方法

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

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

      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 容器中。

负责扫描Mapper接口的扫描器为ClassPathMapperScanner​,他是ClassPathBeanDefinitionScanner​的子类。ClassPathMapperScanner​扫描到注解类后,会为其生成BeanDefinition。这里生成的是一个MapperFactoryBean的bean定义。

  1. 生成bean定义的时候,将bean定义的name设置为userMapper。但是其类型设置为了MapperFactoryBean。
  2. mapperInterface​ 设置为了 com.pansnow.changcheng.mapper.UserMapper
  3. factoryBeanObjectType​设置为了 com.pansnow.changcheng.mapper.UserMapper
  4. 加了个sqlSessionTemplate​的属性,引用指向容器中的bean:sqlSessionTemplate

然后在后期创建bean的时候,MapperFactoryBean会执行getObject方法

  1. knownMapper是一个mapper接口类和他的代理工厂的一个映射。

  2. MapperProxyFactory代理工厂创建mapperProxy 代理mapper接口。

    this.getSqlSession().getMapper(this.mapperInterface);
                ||
               \||/
    this.mapperRegistry.getMapper(type, sqlSession);
                ||
               \||/
    MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
    return mapperProxyFactory.newInstance(sqlSession);
    
  3. mapperProxy在执行方法的时候,会从DefaultSqlSession的Configuration中获取方法对应的MappedStatement。接下来就很明白了。

相关推荐

  1. 1. Spring Boot 自动配置 Mybatis 流程

    2024-01-07 08:54:04       62 阅读
  2. SpringBoot自动配置工作流程中变更自动配置

    2024-01-07 08:54:04       38 阅读
  3. mybatis配置环境流程

    2024-01-07 08:54:04       30 阅读
  4. springboot配置mybatis

    2024-01-07 08:54:04       39 阅读
  5. SpringBoot 自动配置原理

    2024-01-07 08:54:04       62 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-07 08:54:04       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-07 08:54:04       106 阅读
  3. 在Django里面运行非项目文件

    2024-01-07 08:54:04       87 阅读
  4. Python语言-面向对象

    2024-01-07 08:54:04       96 阅读

热门阅读

  1. 《微信小程序开发从入门到实战》学习七十四

    2024-01-07 08:54:04       61 阅读
  2. [设计模式 Go实现] 结构型~装饰模式

    2024-01-07 08:54:04       63 阅读
  3. Qt/QML编程学习之心得:读写GPIO(23)

    2024-01-07 08:54:04       68 阅读
  4. Eureka工作原理及代码实例

    2024-01-07 08:54:04       57 阅读
  5. c# 编程点滴--元组

    2024-01-07 08:54:04       56 阅读
  6. win10、win11安装pytorch(可用)

    2024-01-07 08:54:04       52 阅读
  7. 【React】常用Hook函数的梳理和总结(第二篇)

    2024-01-07 08:54:04       62 阅读
  8. React查询、搜索类功能的实现

    2024-01-07 08:54:04       56 阅读