1. 程式人生 > >MyBatis原始碼閱讀——Spring載入MyBatis過程解析

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類的結構
SqlSessionFactory類結構
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,我們看下這個是什麼:
這裡寫圖片描述
深入程式碼,你就能看到這是過載的build方法,之前程式設計式載入MyBatis的時候呼叫的是上面的方法。
SqlSessionFactory已經建立完成了,那麼,接下來就應該把Mapper裝入Spring容器了。

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

我們是如何從Spring中拿Mapper物件的,這主要是MapperFactoryBean類的作用:
這裡寫圖片描述
這是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就是這樣的流程。