秒杀面试!MyBatis-Spring-Boot 初始化流程深度拆解

graph TD A["MyBatis-Spring-Boot-Starter 依赖"] --> B["自动配置模块
mybatis-spring-boot-autoconfigure"] B --> C["MybatisAutoConfiguration 自动配置类"] %% 数据源依赖 D["Spring 数据源自动配置
(HikariCP/Druid等)"] --> C %% MybatisAutoConfiguration 构建核心组件 C --> E["SqlSessionFactory
(存储全局配置/创建SqlSession)"] C --> F["SqlSessionTemplate
(线程安全的Sql执行器)"] E --> F["SqlSessionTemplate"] %% 事务整合 G["SpringManagedTransactionFactory"] --> E["SqlSessionFactory"] H["Spring 事务管理器
(@Transactional)"] --> G %% Mapper扫描与代理 I["@MapperScan 注解"] --> J["MapperScannerRegistrar"] J --> K["MapperScannerConfigurer
(扫描Mapper接口)"] K --> L["Mapper接口
(com.test.core.mapper)"] F --> M["Mapper代理对象
(MyBatis动态代理)"] L --> M %% 配置文件关联 N["application.yml
(mybatis前缀配置)"] --> C O["mybatis-config.xml
(全局配置)"] --> E P["Mapper XML映射文件
(mapper/**/*.xml)"] --> E %% 业务层调用 M --> Q["Service层
(@Autowired注入Mapper)"] %% 样式优化 classDef config fill:#f0f8ff,stroke:#4169e1,stroke-width:2px classDef core fill:#fdf2f8,stroke:#9c27b0,stroke-width:2px classDef scan fill:#e8f4f8,stroke:#00bcd4,stroke-width:2px classDef business fill:#f5f5f5,stroke:#666,stroke-width:2px class B,C,N,O config class E,F,G,H core class I,J,K,L,M scan class Q business

一、基本使用(含场景说明与注意事项)

MyBatis-Spring-Boot-Starter 简化了 MyBatis 与 Spring Boot 的整合,无需手动配置 SqlSessionFactory、SqlSession 等核心组件,仅需三步即可快速集成。

1. 依赖引入

在 Maven 项目的 pom.xml 中添加核心依赖,该依赖已内置 MyBatis 核心包、Spring 整合适配包及自动配置模块,无需额外引入其他关联依赖。

xml 复制代码
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.0</version> <!-- 稳定版,适配 Spring Boot 2.x 系列,如需适配 3.x 可升级至 3.0+ 版本 -->
</dependency>

2. YML 核心配置

通过 application.yml 配置 MyBatis 核心参数,指定配置文件路径和 Mapper 映射文件位置,支持通配符匹配多目录下的映射文件。

yaml 复制代码
mybatis:
  config-location: classpath:mybatis-config.xml # 指定 MyBatis 全局配置文件(如别名、插件、缓存等配置)
  mapper-locations: classpath*:mapper/**/*.xml # 扫描类路径下所有 mapper 目录(含子目录)的 XML 映射文件
  type-aliases-package: com.test.core.entity # 可选配置,指定实体类包路径,XML 中可直接使用类名(无需全限定名)
  configuration:
    map-underscore-to-camel-case: true # 可选配置,开启下划线命名转驼峰命名(如 user_name → userName)

3. 启动类配置

在 Spring Boot 启动类上添加 @MapperScan 注解,指定 Mapper 接口所在包路径,Spring 会自动扫描该包下的接口并创建代理对象。

java 复制代码
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

// 开启 Spring Boot 自动配置,扫描当前包及子包下的组件
@SpringBootApplication
// 扫描 Mapper 接口所在包,多个包可通过逗号分隔(如 "com.test.core.mapper,com.test.ext.mapper")
@MapperScan(basePackages = "com.test.core.mapper")
public class TestApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        // 启动 Spring Boot 应用,加载所有自动配置和自定义组件
        SpringApplication.run(TestApplication.class, args);
    }

}

二、初始化流程(底层原理拆解)

Spring Boot 启动时,MyBatis 会通过自动配置机制完成核心组件的初始化,最终实现 Mapper 接口与 XML 映射文件的关联,核心是构建三大关键对象并注入 Spring 容器。

1. 核心初始化目标

初始化过程的核心是创建并组装三个核心对象,支撑 MyBatis 完整的 SQL 执行流程:

  • SqlSessionFactory: MyBatis 核心工厂类,保存全局配置(数据源、映射文件、缓存策略等),负责创建 SqlSession 实例。
  • SqlSessionTemplate: SqlSession 的 Spring 适配版(线程安全),封装了 SqlSession 的核心操作(CRUD),供业务代码直接调用。
  • MapperScannerConfigurer: Mapper 接口扫描器,扫描指定包下的 Mapper 接口,为每个接口创建动态代理对象并注册到 Spring 容器。

2. SqlSessionFactory 与 SqlSessionTemplate 的注入流程

这两个组件的初始化依赖 Spring Boot 的自动配置机制,核心入口是 MybatisAutoConfiguration 类。

(1)自动配置类加载 引入 mybatis-spring-boot-starter 后,依赖会自带 mybatis-spring-boot-autoconfigure-2.1.0.jar。

Spring Boot 启动时,会通过 META-INF/spring.factories 文件中的配置,自动加载 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration 这个自动配置类。

(2)SqlSessionFactory 构建

MybatisAutoConfiguration 类中通过 @Bean 注解声明 SqlSessionFactory,核心逻辑如下:

java 复制代码
public class MybatisAutoConfiguration implements InitializingBean {
    // 注入配置文件中的参数(对应 yml 中 mybatis 前缀的配置)
    private final MybatisProperties properties;
    // 注入数据源(由 Spring 自动配置,如 Druid、HikariCP 等)
    private final DataSource dataSource;
    // 其他依赖组件(拦截器、类型处理器等)
    private final Interceptor[] interceptors;
    private final List<ConfigurationCustomizer> configurationCustomizers;

    // 构造方法自动注入依赖
    public MybatisAutoConfiguration(MybatisProperties properties, 
                                    ObjectProvider<Interceptor[]> interceptorsProvider,
                                    ObjectProvider<TypeHandler[]> typeHandlersProvider,
                                    ResourceLoader resourceLoader,
                                    ObjectProvider<DatabaseIdProvider> databaseIdProvider,
                                    ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
        this.properties = properties;
        this.interceptors = interceptorsProvider.getIfAvailable();
        this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable(Collections::emptyList);
        // 其他赋值逻辑...
    }

    // 构建 SqlSessionFactory 并注入 Spring 容器,@ConditionalOnMissingBean 表示用户未自定义时才生效
    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        // 设置数据源
        factory.setDataSource(dataSource);
        // 设置 Mapper 映射文件路径(从配置文件中读取)
        factory.setMapperLocations(resolveMapperLocations(properties.getMapperLocations()));
        // 设置全局配置(如别名、下划线转驼峰等)
        applyConfiguration(factory);
        // 设置拦截器(如分页插件、日志插件等)
        if (this.interceptors != null && this.interceptors.length > 0) {
            factory.setPlugins(this.interceptors);
        }
        // 其他配置(类型处理器、数据库方言等)...
        // 构建 DefaultSqlSessionFactory 实例并返回
        return factory.getObject();
    }

    // 应用自定义配置(如通过 ConfigurationCustomizer 接口修改 MyBatis 配置)
    private void applyConfiguration(SqlSessionFactoryBean factory) {
        Configuration configuration = this.properties.getConfiguration();
        // 若未指定 mybatis.config-location,则创建默认 Configuration 对象
        if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
            configuration = new Configuration();
        }
        // 执行自定义配置(用户可实现 ConfigurationCustomizer 接口扩展配置)
        if (configuration != null && !this.configurationCustomizers.isEmpty()) {
            for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
                customizer.customize(configuration);
            }
        }
        factory.setConfiguration(configuration);
    }

    // 构建 SqlSessionTemplate 并注入容器,依赖 SqlSessionFactory
    @Bean
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        // 默认为批量操作模式,可通过构造参数指定执行器类型(SIMPLE/REUSE/BATCH)
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

(3)Spring 事务整合

MyBatis 的事务由 TransactionFactory 接口管理,SqlSessionFactoryBean 的 buildSqlSessionFactory 方法中,会默认使用 SpringManagedTransactionFactory 适配 Spring 事务:

java 复制代码
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
    Configuration targetConfiguration = new Configuration();
    // 配置环境:关联数据源和 Spring 事务工厂
    targetConfiguration.setEnvironment(new Environment(
        this.environment,
        // 若未自定义事务工厂,默认使用 SpringManagedTransactionFactory
        this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
        this.dataSource
    ));
    // 其他构建逻辑...
    return new DefaultSqlSessionFactory(targetConfiguration);
}

这使得 MyBatis 可以无缝集成 Spring 的声明式事务(如 @Transactional 注解)。

3. MapperScannerConfigurer 的注入流程

Mapper 接口的扫描与代理对象创建,核心依赖 @MapperScan 注解和 MapperScannerRegistrar 类。

(1)注解触发扫描

@MapperScan 注解中通过 @Import(MapperScannerRegistrar.class) 导入扫描注册器,触发 Mapper 扫描逻辑:

java 复制代码
// 可重复注解,支持多包扫描
@Repeatable(MapperScans.class)
@Import(MapperScannerRegistrar.class) // 导入扫描注册器
public @interface MapperScan {
    String[] basePackages() default {}; // Mapper 接口所在包路径
    Class<?>[] basePackageClasses() default {}; // 替代包路径,指定类所在包
    // 其他属性(如 sqlSessionTemplateRef、sqlSessionFactoryRef 等)...
}

(2)注册 MapperScannerConfigurer

MapperScannerRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,在 Spring 容器初始化时,会动态注册 MapperScannerConfigurer 到容器:

java 复制代码
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    private ResourceLoader resourceLoader;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 解析 @MapperScan 注解的属性(如 basePackages)
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(
            importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())
        );
        // 注册 MapperScannerConfigurer 到 Spring 容器
        registerBeanDefinitions(annoAttrs, registry, generateBeanName(annoAttrs, registry));
    }

    void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
        // 构建 MapperScannerConfigurer 的 BeanDefinition
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
        // 设置扫描的包路径
        builder.addPropertyValue("basePackage", StringUtils.arrayToCommaDelimitedString(annoAttrs.getStringArray("basePackages")));
        // 设置 SqlSessionTemplate 的引用(默认使用容器中的 sqlSessionTemplate)
        if (annoAttrs.hasAttribute("sqlSessionTemplateRef")) {
            builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
        }
        // 设置 SqlSessionFactory 的引用(默认使用容器中的 sqlSessionFactory)
        if (annoAttrs.hasAttribute("sqlSessionFactoryRef")) {
            builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
        }
        // 其他配置(如标记接口、过滤器等)...
        // 注册 BeanDefinition 到 Spring 容器
        registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
    }

    // 其他辅助方法...
}

(3)Mapper 接口代理与注册

MapperScannerConfigurer 初始化后,会扫描 basePackages 下的所有 Mapper 接口,通过 MyBatis 的 MapperProxyFactory 为每个接口创建动态代理对象,最终将代理对象注册到 Spring 容器。

业务代码可直接通过 @Autowired 注入 Mapper 接口,实际调用的是代理对象,由代理对象通过 SqlSessionTemplate 执行 SQL。

相关推荐
用户8307196840822 小时前
Shiro登录验证与鉴权核心流程详解
spring boot·后端
d***81722 小时前
springboot 修复 Spring Framework 特定条件下目录遍历漏洞(CVE-2024-38819)
spring boot·后端·spring
2***d8852 小时前
Spring Boot中的404错误:原因、影响及处理策略
java·spring boot·后端
c***69302 小时前
Springboot项目:使用MockMvc测试get和post接口(含单个和多个请求参数场景)
java·spring boot·后端
6***A6632 小时前
Springboot中SLF4J详解
java·spring boot·后端
W***r262 小时前
SpringBoot整合easy-es
spring boot·后端·elasticsearch
5***84643 小时前
Spring Boot的项目结构
java·spring boot·后端
6***83053 小时前
SpringBoot教程(三十二) SpringBoot集成Skywalking链路跟踪
spring boot·后端·skywalking
Coder_Boy_3 小时前
Flutter基础介绍-跨平台移动应用开发框架
spring boot·flutter