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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料

我们平时在项目中都是用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类的结构
20191123100217\_1.png
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,我们看下这个是什么:
20191123100217\_2.png
深入代码,你就能看到这是重载的build方法,之前编程式加载MyBatis的时候调用的是上面的方法。
SqlSessionFactory已经创建完成了,那么,接下来就应该把Mapper装入Spring容器了。

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

我们是如何从Spring中拿Mapper对象的,这主要是MapperFactoryBean类的作用:
20191123100217\_3.png
这是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就是这样的流程。


来源:http://ddrv.cn/a/88268

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » MyBatis源码阅读——Spring加载MyBatis过程解析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏