美文网首页Java架构技术进阶Java成长之路
【转】理解 MyBatis 是如何在 Spring 容器中初始化

【转】理解 MyBatis 是如何在 Spring 容器中初始化

作者: 程序员北游 | 来源:发表于2020-11-09 20:31 被阅读0次

    MyBatis 初始化过程就是生成一些必须的对象放到 Spring 容器中。问题是这个过程到底生成了哪些对象?当遇到 MyBatis 初始化失败时,如何正确的找到分析问题的切入点?本文将针对这些问题进行介绍。

    本文基于 MyBatis 3和 Spring,假设读者已经知道如何使用 Maven 和 MyBatis,以及了解 Spring 的容器机制。

    一、Mybatis 三件套

    我们知道 MyBatis 的主要功能是由 SqlSessionFactory 和 Mapper 两者提供的,初始化 MyBatis 就是初始化这两类对象。除此之外 DataSource 作为数据库访问对象也是必不可少。因此首先我们应该记住 MyBatis 初始化的核心三件套:

    • DataSource:它是访问数据库所必须的数据源对象,这个初始化失败就无法直接访问数据库。
    • SqlSessionFactoryBean:这是在 Spring 容器中对 SqlSessionFactory 初始化过程的封装。
    • MapperScannerConfigurer:这是在 Spring 容器中对 Mapper 初始化过程的封装。

    具体来说,一个简单的初始化过程就是下面这样:

    @Configuration
    public class SpringMyBatisApplication {
        public static void main(String[] args) {
            new AnnotationConfigApplicationContext(SpringMyBatisApplication.class);
        }
        @Bean
        public DataSource dataSource() {
            return ...;
        }
        @Bean
        public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
            return ...;
        }
        @Bean
        public MapperScannerConfigurer mapperScannerConfigurer() {
            return ...;
        }
    }
    

    接下来介绍三件套各自如何初始化,下面的内容是可以实际操作的,不妨动手试试。

    1. DataSource 初始化

    首先我们创建一个空的 Maven 项目,在 pom.xml 中加入下面的依赖关系:

    <!-- Spring -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.2.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>5.2.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.0.RELEASE</version>
    </dependency>
    
    <!-- 数据库 -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-dbcp2</artifactId>
      <version>2.7.0</version>
    </dependency>
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>1.4.199</version>
    </dependency>
    

    本文重在演示 MyBatis 的初始化过程,所以没有复杂的 SQL,数据库用的是嵌入式数据库 h2。

    然后我们在 com.hyd.mybatis3test 包下面创建一个 SpringMyBatisApplication 类,代码在前面给过了。

    对应的 DataSource 初始化实现如下:

    @Bean
    public DataSource dataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("org.h2.Driver");
        dataSource.setUrl("jdbc:h2:mem:test");
        return dataSource;
    }
    

    2. SqlSessionFactoryBean 初始化

    SqlSessionFactoryBean 是对 SqlSessionFactory 初始化过程的封装,Spring 会在适当的时候执行这个初始化过程,得到最终的 SqlSessionFactory 对象。

    SqlSessionFactoryBean 的创建过程如下(注意方法签名在前面的基础上有变动):

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(
            DataSource dataSource,
            ResourcePatternResolver resolver
    ) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(resolver.getResources("classpath*:mappers/*.xml"));
        return bean;
    }
    

    其中:

    • 第一个参数 dataSource 就是前面生成的数据源对象;
    • 第二个参数 resolver 是 Spring 自动提供的,用于搜索指定路径下的所有 xml 文件。本文不会包含 xml 文件,所以这个配置是无效的,这行可以不写,不过写了也不影响程序运行。

    3. MapperScannerConfigurer 初始化

    MapperScannerConfigurer 的职责是在指定路径下搜索所有的 Mapper 接口类(参考它的 postProcessBeanDefinitionRegistry() 方法),并通过 MapperFactoryBean 将其注册到 MapperRegistry 中。

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer configurer = new MapperScannerConfigurer();
        configurer.setBasePackage("com.hyd.mybatis3test");
        return configurer;
    }
    

    4. 验证初始化过程成功

    为了验证上面的初始化过程完成了,我们在 com.hyd.mybatis3test 包下面创建一个 Mapper 类:

    @Mapper
    public interface SampleMapper {
        @Update("create table if not exists user(id int)")
        void createUserTable();
    }
    

    以及一个 Service 类:

    @Service
    public static class SampleService {
        @Autowired
        private SampleMapper sampleMapper;
        @PostConstruct
        public void init() {
            sampleMapper.createUserTable();
        }
    }
    

    然后别忘了在 SpringMyBatisApplication 顶上添加一个 @ComponentScan("com.hyd.mybatis3test") 注解,否则 Spring 会找不到 SampleService。

    运行 SpringMyBatisApplication.main() 方法,我们就能在输出中找到这样的内容:

    ...
    SampleMapper.createUserTable - ==>  Preparing: create table if not exists user(id int)
    SampleMapper.createUserTable - ==> Parameters:
    SampleMapper.createUserTable - <==    Updates: 0
    ...
    

    这说明这条创建表格的 SQL 语句成功执行了。

    在前面三件套的基础上,MyBatis 也提供了更多的封装。有了本文上面的铺垫,相信读者对这些封装方式理解起来也会轻松很多。

    二、@MapperScan 注解

    @MapperScan 注解只不过是 MapperScannerConfigurer 的启动器而已,使用这个注解,可以代替前面的 MapperScannerConfigurer 初始化。

    三、SpringBoot 自动初始化

    MyBatis 提供 mybatis-spring-boot-starter 库用于在 Spring Boot 项目中自动初始化:

    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>2.1.3</version>
    </dependency>
    

    这个所谓的自动初始化实际上就是初始化 SqlSessionFactory 对象。初始化的过程由 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration 完成,所需的配置都从 "mybatis-" 前缀的配置属性中获取,具体可以参考 org.mybatis.spring.boot.autoconfigure.MybatisProperties类。

    总结

    总之,MyBatis 的初始化核心过程就是三件套的初始化。而在 Spring Boot 应用中,结合自动初始化和 @MapperScan 注解,我们无需手工初始化上这三件套,就能直接从容器中得到 Mapper 对象。

    原文地址:https://segmentfault.com/a/1190000037770470

    相关文章

      网友评论

        本文标题:【转】理解 MyBatis 是如何在 Spring 容器中初始化

        本文链接:https://www.haomeiwen.com/subject/xqyrbktx.html