MyBatis源码阅读——Spring加载MyBatis过程解析

我们平时在项目中都是用Spring来管理的,那么,Spring是如何管理MyBatis的呢?我们来一探究竟。

编程式加载MyBatis

要了解Spring是如何加载MyBatis的,我想还是先来回顾一下我们是如何用编程的方式去加载MyBatis框架的

 String resource = "mybatis/conf/mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //从 XML 中构建 SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //获取session
        SqlSession session = sqlSessionFactory.openSession();
        try {
            //获取Mapper
            BlogMapper mapper = session.getMapper(BlogMapper.class);
            Blog blog = mapper.selectBlog(1L);
            System.out.println(blog);
//            blog = mapper.selectBlog(1L);
//            System.out.println(blog);
        } finally {
            session.close();
        }

在代码中,我们主要是构建SqlSessionFactory,而构建 SqlSessionFactory,需要配置文件。那么Spring中我们是怎么使用的呢?

Spring配置Mybatis

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--注入数据库连接池 -->
        <property name="dataSource" ref="dataSource"/>
        <!--配置mybatis全局配置文件:mybatis-config.xml -->
        <property name="configLocation" value="classpath:mybatis/conf/mybatis-config.xml"/>
        <!--扫描entity包,使用别名,多个用;隔开 -->
        <property name="typeAliasesPackage" value="entity"/>
        <!--扫描sql配置文件:mapper需要的xml文件 -->
        <property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"/>
         <!--配置插件 -->
        <property name="plugins">
            <array>
                <bean class="com.github.pagehelper.PageHelper">
                    <property name="properties">
                        <value>
                            dialect=mysql
                        </value>
                    </property>
                </bean>
            </array>
        </property>
    </bean>

    <!-- 配置扫描Dao接口包,动态实现Dao接口,并注入到spring容器中 -->
  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="top.yuyufeng.learn.mybatis.mapper"/>
    </bean>

从配置中,我们可以看出,这里做了两步操作:

  • 构建SqlSessionFactory
  • 把Mappers注入到Spring容器管理

Spring 加载SqlSessionFactory的过程:

首先我们看下SqlSessionFactory类的结构
《MyBatis源码阅读——Spring加载MyBatis过程解析》
SqlSessionFactory实现了InitializingBean接口,也就是说,在该Bean初始化的时候,我执行InitializingBean中的afterPropertiesSet()方法。那么,我们直接进入SqlSessionFactory的afterPropertiesSet()方法:

  public void afterPropertiesSet() throws Exception {
    //...
    //构建SqlSessionFactory
    this.sqlSessionFactory = buildSqlSessionFactory();
  }

在该方法中,直接进行构建SqlSessionFactory,那么来看下构建SqlSessionFactory的过程方法(删去了部分):

  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;

    XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);

    configuration = xmlConfigBuilder.getConfiguration();

    configuration.setObjectFactory(this.objectFactory);

   configuration.setObjectWrapperFactory(this.objectWrapperFactory);

    String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeAliasPackageArray) {
        configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
      }

      for (Class<?> typeAlias : this.typeAliases) {
        configuration.getTypeAliasRegistry().registerAlias(typeAlias);
      }

      for (Interceptor plugin : this.plugins) {
        configuration.addInterceptor(plugin);
      }

      String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeHandlersPackageArray) {
        configuration.getTypeHandlerRegistry().register(packageToScan);
      }

      for (TypeHandler<?> typeHandler : this.typeHandlers) {
        configuration.getTypeHandlerRegistry().register(typeHandler);
      }

    xmlConfigBuilder.parse();

    configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));

    configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));


      for (Resource mapperLocation : this.mapperLocations) {
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
              configuration, mapperLocation.toString(), configuration.getSqlFragments());
          xmlMapperBuilder.parse();
      }
    }
    return this.sqlSessionFactoryBuilder.build(configuration);
  }

//在上面的方法中主要进行的操作是:整理加载配置信息Configuration,使用这些配置通过SqlSessionFactoryBuilder创建SqlSessionFactory,在最后调用了sqlSessionFactoryBuilder.build,我们看下这个是什么:
《MyBatis源码阅读——Spring加载MyBatis过程解析》
深入代码,你就能看到这是重载的build方法,之前编程式加载MyBatis的时候调用的是上面的方法。
SqlSessionFactory已经创建完成了,那么,接下来就应该把Mapper装入Spring容器了。

Mappers注入到Spring容器管理的过程

我们是如何从Spring中拿Mapper对象的,这主要是MapperFactoryBean类的作用:
《MyBatis源码阅读——Spring加载MyBatis过程解析》
这是Mapper的获取过程:

  /**
   * {@inheritDoc}
   */
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

接下来就是Mappers注入到Spring容器管理的过程了:
它实现了BeanDefinitionRegistryPostProcessor接口,MapperScannerConfigurer->postProcessBeanDefinitionRegistry()

BeanDefinitionRegistryPostProcessor 是 BeanFactoryPostProcessor 的子类,可以通过编码的方式,改变、新增类的定义,甚至删除某些 bean 的定义

  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }

    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.registerFilters();
    //把定义的mapper包下的所有bean注册到Spring容器中
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

总结

Spring加载MyBatis这个过程,其实就是把MyBatis的Mapper转换成Bean,注入到Spring容器的过程。学习这些内容不只是帮助我们了解MyBatis是怎么样结合Spring的,也让我们了解或许大多数框架结合Spring就是这样的流程。

    原文作者:Spring Cloud
    原文地址: https://blog.csdn.net/qq_18860653/article/details/80662593
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞