【MyBatis】MyBatis是怎么在Spring Boot中初始化的?

目的

  1. 复习Spring Boot项目中IoC加载的主要流程
  2. 了解如何配置自定义的AutoConfiguration
  3. (重点) 了解MyBatis如何在Spring Boot项目中加载配置和初始化

注:本文针对的是MyBatis和Spring Boot关联内容,没有详细讲解MyBatis本身的配置和初始化,这部分会在专题的其他文章中再详述。

先修知识

  1. 需要简单了解Spring Boot的IoC加载主要流程
  2. 需要了解如何使用MyBatis
  3. 最好了解单纯的Java+MyBatis项目中,MyBatis如何加载配置和初始化

sample项目

直接用官网提供的Quick-Start的Sample项目做本文的样例项目。别管这个项目有多简单,只有真的上手去跑,去看代码,才有感觉。

Quick Start · mybatis/spring-boot-starter Wiki · GitHub

使用下面的curl命令拉取sample项目后,按照上面官方Wiki文档的内容依次将所需文件内容写入项目中

curl -s https://start.spring.io/starter.tgz\
       -d name=mybatis-sample\
       -d artifactId=mybatis-sample\
       -d dependencies=mybatis,h2\
       -d baseDir=mybatis-sample\
       -d type=maven-project\
       | tar -xzvf -

这个项目的基本信息:

jdk17,spring-boot 3.2.4,mybatis-spring-boot-starter 3.0.3,数据库使用内存数据库h2

mybatis-spring-boot-starter 3.0.3 这个MAVEN包中又包含:

  • mybatis
  • mybatis-spring
  • mybatis-spring-boot-autoconfigure
  • spring-boot-starter
  • spring-boot-starter-jdbc

Sample项目运行结果:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.4)

2024-04-01T16:47:21.323+08:00  INFO 66794 --- [mybatis-sample] [           main] c.e.m.MybatisSampleApplication           : Starting MybatisSampleApplication using Java 17 with PID 66794 (/Users/xxx/Documents/study/mybatis_study/mybatis-sample/target/classes started by zhuangsuyu in /Users/xxx/Documents/study/mybatis_study/mybatis-sample)
2024-04-01T16:47:21.326+08:00  INFO 66794 --- [mybatis-sample] [           main] c.e.m.MybatisSampleApplication           : No active profile set, falling back to 1 default profile: "default"
2024-04-01T16:47:22.158+08:00  INFO 66794 --- [mybatis-sample] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-04-01T16:47:22.314+08:00  INFO 66794 --- [mybatis-sample] [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:a1672050-cc5a-4f4f-b0c9-b28b3b635f20 user=SA
2024-04-01T16:47:22.315+08:00  INFO 66794 --- [mybatis-sample] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-04-01T16:47:22.585+08:00  INFO 66794 --- [mybatis-sample] [           main] c.e.m.MybatisSampleApplication           : Started MybatisSampleApplication in 1.594 seconds (process running for 1.997)
1,San Francisco,CA,US
2024-04-01T16:47:22.648+08:00  INFO 66794 --- [mybatis-sample] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2024-04-01T16:47:22.651+08:00  INFO 66794 --- [mybatis-sample] [ionShutdownHook] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

AutoConfiguration的配置

auto-configuration官方文档

我们知道Spring Boot的设计核心原则之一是简化配置(约定优于配置):让一些常用的配置直接默认,如果是特殊需要定制的配置,用户可以用自己的Bean实例去替换。

而实现“约定优于配置”这一原则的核心功能之一就是AutoConfiguration

在不同的Spring Boot版本中,AutoConfiguration的写法略有不同,下面展示的是不同版本中,Mybatis官方提供的MybatisAutoConfiguration源码配置方式,稍作了解即可。

在spring-boot.2.7.x 之前版本里

在目录 META-INF/下新建文件 spring.factories

文件中内容填写:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

在spring-boot.2.7.x 之后版本里

在目录 META-INF/spring/下新建文件 org.springframework.boot.autoconfigure.AutoConfiguration.imports

文件中内容填写:

org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

简述SpringIoC的加载过程

在Spring Boot启动过程中,我们需要知道AbstractApplicationContext类中有个非常关键的refresh()方法。

refresh()方法中又有两个重要的方法:

  1. invokeBeanFactoryPostProcessors(beanFactory);调用已经注册过了的Bean工厂后置处理器,用来解析和加载各种配置类,注册到beanDefinitionMap
    • 先分别getBean之前的Bean工厂后置处理器(如internalConfigurationAnnotationProcessor)
    • 然后利用ConfigurationClassPostProcessor这个Bean工厂后置处理器,解析各种加了 @Configuration的配置类, @ComponentScan包, @Import 注解等
  2. finishBeanFactoryInitialization(beanFactory);实例化所有剩余的单例bean
    1. 利用反射的方式实例化
    2. 根据DI,填充属性
    3. 初始化

以下是一个常见的refresh()方法,粘贴在这里帮助大家对我们要讲的两个重点方法在哪个位置有个大致印象。

@Override
public void refresh() throws BeansException, IllegalStateException {
    this.startupShutdownLock.lock();
    try {
        // 一些准备工作...
        try {
            postProcessBeanFactory(beanFactory);
            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
       

            // 【重点】调用ConfigurationClassPostProcessor解析配置类,会注册生成beanDefinitionMap
            invokeBeanFactoryPostProcessors(beanFactory);
          

            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();
            initMessageSource();
            initApplicationEventMulticaster();
            onRefresh();
            registerListeners();


            // 【重点】 Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);


            finishRefresh();
        }

        catch (RuntimeException | Error ex ) {
           // 一些catch工作...
        } finally {
            contextRefresh.end();
        }
    } finally {
        this.startupShutdownThread = null;
        this.startupShutdownLock.unlock();
    }
}

源码总体流程图

从refresh()方法中的两个重点方法出发,一步步会通过默认配置、用户自定义配置将MyBatis需要的信息加载到框架中。

这里我先将我整理的源码总体流程图贴在这里,后面的内容会详细、局部地去说明各个环节的执行流程。

图中左上方灰色方块底的模块是发生在Spring Boot包中的,绿色方块底的模块发生在mybatis-spring包中,图正中偏右下的红色方块底是mybatis-spring-boot-autoconfigure提供的能力,蓝紫色方块底是最单纯的mybatis包本身了。

mybatis-spring包在各个包中起到了连接作用。

xml配置文件的解析流程

先说xml配置文件。xml配置文件一般长下面这样,文件本身放在项目的resources目录下。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
                <dataSource type="POOLED">
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://xxx"/>
                    <property name="username" value="xxxx"/>
                    <property name="password" value="xxxx"/>
                </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="aaaa/bbbb.xml"/>
    </mappers>
</configuration>

如果配置了mybatis-config.xml配置文件,该文件的解析和配置是在本文所述的refresh()第二个核心方法(即实例化各个bean的阶段)发生的,如下图所示。

在调用SqlSessionFactoryBeanfactory.getObject()方法时,会在buildSqlSessionFactory()方法中进行parse,使用mybatis包中的XMLConfigBuilder解析xml配置文件。

当然,项目也可能自己注入了一个自定义的SqlSessionFactory,仍然也是会调用到factory.getObject()方法的,如下所示:

package com.example.mybatissample.config;


@Configuration
@MapperScan("com.example.mybatissample")
public class MybatisConfig {

    @Autowired
    DataSource dataSource;

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean(
            @Value("classpath:/mybatis/mybatis-config.xml") Resource configLocation
    ) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setConfigLocation(configLocation);
        return sqlSessionFactoryBean.getObject();
    }
}

当然,在实际项目中,我们也可能完全不写xml配置文件。这样便于管理配置信息,避免配置分散在不同文件里。

Mapper接口和xml映射文件的扫描加载

mapper接口

我们知道有以下两种配置mapper接口类的方法,这些配置方式到底是怎么影响扫描的?

  • @MapperScan,配合@Repository(后者可以不配置,不写最多可能会导致有的版本的idea在注入的地方有红色波浪线)
  • @Mapper,如果不使用@MapperScan统一扫描包,那么每个mapper接口上都一定要注解@Mapper。注意,在使用了@MapperScan的情况下,自定义的包外面单独的@Mapper就没用了。

所以在大型项目中,一般都会采用@MapperScan的方式。

在没有@MapperScan的情况下,AutoConfiguredMapperScannerRegistrar类中的registerBeanDefinitions()方法会在load阶段调用,用来生成默认的MapperScannerConfigurer类的beanDefinition,该默认配置中的配置的basePackage是整个项目的Application类所在的包。

mapper接口的扫描也发生在SpringIoC的生成beanDefinition阶段,通过MapperScannerConfigurer

在有@MapperScan的情况下

MybatisAutoConfiguration 类下的 MapperScannerRegistrarNotFoundConfiguration 类的 ConditionalOnMissingBean 条件不满足,所以不会注册默认的MapperScannerConfigurer类的beanDefinition,而是使用用户自定义的方式。

  @org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
  @Import(AutoConfiguredMapperScannerRegistrar.class)
  @ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
  public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
      logger.debug(
          "Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
    }

  }

mapper接口的映射文件(xml)

配置mapper接口的映射文件

  • setMapperLocations
  • 在xml配置文件中<mappers>下的<mapper resource="">标签<mapper resdource="/mybatis/mapper/StudentMapperDAO.xml"/>
  • 接口文件与映射文件在同一路径下,接口名与映射文件名相同,并且映射文件命名为接口类名时, 在xml配置文件中<mappers>下的<mapper class="">标签 <mapper class="com.example.mybatissample.mapper.StudentMapper"/>
  • 接口文件与映射文件在同一路径下,接口名与映射文件名相同,并且映射文件命名为接口类名时, 在xml配置文件中<mappers>下的<package>标签<mapper class="com.example.mybatissample.mapper"/>

映射文件的解析是在的refresh()第二个核心方法(即实例化各个bean的阶段)发生的解析的。

使用mybatis包中的XMLConfigBuilder解析xml配置文件的最后,会交由XMLMapperBuilder类负责映射文件的解析,生成mappedStatements存放在configuration对象中。这部分就是纯mybatis源码里的内容了,不在这里赘述。

    private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
            this.loadCustomVfsImpl(settings);
            this.loadCustomLogImpl(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginsElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlersElement(root.evalNode("typeHandlers"));
            this.mappersElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

总结

 在这篇文章里,我们先梳理了Spring Boot IoC的基本流程,确定了refresh()中两个最重点的方法。通过将这两个重点方法与MyBatis配置加载和初始化相结合的方式,明确了xml配置文件、Mapper接口以及映射文件的加载、解析分别发生在这两个方法的什么环节。

换句话说,本质上我们是先在脑海中通过Spring Boot IoC的基本流程绘制了一张简要地图,然后在这张地图中圈出了哪些地点是MyBatis加载、解析、初始化的重要地方。

核心知识点简要总结,Spring Boot IoC的基本流程里refresh()中有两个重点方法,分别是为了形成beanDefinitionMap的invokeBeanFactoryPostProcessors(beanFactory);和为了实例化各种剩余bean的finishBeanFactoryInitialization(beanFactory)mapper接口的扫描发生在第1个重点方法;xml配置文件的解析和xml映射文件的解析,都发生在第2个重点方法中。

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Spring BootMyBatis是两个非常流行的Java框架,它们可以很好地协同工作。Spring Boot提供了一个快速开发的框架,MyBatis则提供了一个强大的ORM框架。将它们整合在一起可以让我们更快地开发出高效的Java应用程序。 整合Spring BootMyBatis需要以下步骤: 1. 添加依赖:在pom.xml文件添加Spring BootMyBatis的依赖。 2. 配置数据源:在application.properties文件配置数据库连接信息。 3. 配置MyBatis:在配置类添加MyBatis的配置信息。 4. 编写Mapper接口:创建Mapper接口,并在其定义SQL语句。 5. 编写Mapper.xml文件:在resources目录下创建Mapper.xml文件,并在其编写SQL语句。 6. 注册Mapper接口:在配置类注册Mapper接口。 7. 测试:编写测试类,测试MyBatis是否正常工作。 以上就是整合Spring BootMyBatis的基本步骤。通过这种方式,我们可以快速地开发出高效的Java应用程序。 ### 回答2: SpringBoot是一个用于开发Java应用程序的开源框架,它提供了很多简化开发的特性。而MyBatis是一个流行的持久层框架,用于访问数据库。将SpringBootMyBatis进行整合,可以更加方便地开发数据库相关的应用。 首先,需要在项目的pom.xml文件添加SpringBootMyBatis的依赖。然后,配置数据库连接信息,可以通过application.properties或application.yml文件进行配置。 接下来,创建实体类和Mapper接口。实体类用于映射数据库表的字段,而Mapper接口用于定义数据库操作的方法。 然后,编写Mapper接口的实现类。可以使用注解或XML文件来编写SQL语句,从而实现对数据库的增删改查操作。 在SpringBoot的启动类,使用@MapperScan注解来扫描Mapper接口,并将其注册到Spring容器。 最后,就可以在业务逻辑使用@Autowired注解来注入Mapper接口的实例,从而调用数据库的操作方法。 整合完成后,可以简化数据库操作的代码,提高开发效率。同时,SpringBoot的自动配置和快速启动特性,让整合更加方便和高效。 总结起来,SpringBootMyBatis的整合可以帮助开发者更好地开发数据库相关的应用,提供了便捷的配置和快速的启动特性,提高开发效率。 ### 回答3: Spring BootMyBatis是两个非常流行的Java开发框架。Spring Boot是一个用于开发各种Java应用程序的框架,它可以使开发者更简单地搭建和配置应用程序。而MyBatis是一个流行的持久层框架,它可以帮助开发者更方便地操作数据库。 Spring BootMyBatis的整合过程相对简单,以下是一些主要步骤: 1. 创建一个Spring Boot项目,可以选择使用Spring Initializr进行项目的初始化。 2. 在项目的pom.xml文件添加MyBatis和相应的数据库驱动依赖。 3. 创建一个MyBatis的配置文件,可以命名为mybatis-config.xml,用于配置MyBatis的一些全局参数和属性。 4. 创建一个数据源配置类,该类需要使用Spring Boot提供的数据源配置,以及MyBatis的参数配置。 5. 创建一个mapper接口,该接口定义了数据库操作的方法,并使用MyBatis的注解来映射SQL语句。 6. 创建对应的mapper XML文件,该文件用于编写SQL语句,并将SQL语句与mapper接口的方法进行绑定。 7. 在Spring Boot的配置文件配置扫描mapper文件和接口的路径。 8. 在需要使用数据库操作的地方,通过@Autowired注解将mapper接口注入,并通过调用接口的方法来进行数据库操作。 整合完成后,可以通过调用mapper接口的方法来执行数据库操作,MyBatis会自动将SQL语句与相应的数据库操作进行绑定,从而实现对数据库的增删改查等操作。 总的来说,Spring BootMyBatis的整合过程相对简单,只需要进行一些配置和创建相应的接口和XML文件即可。这样可以充分发挥Spring BootMyBatis的优势,使开发者更方便地进行数据库操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值