1. 程式人生 > 實用技巧 >Mybatis和Spring的整合原理

Mybatis和Spring的整合原理

  上一篇提到了和Spring整合後,Mybatis的BatchExecutor無法真正生效,本篇就好好分析分析這裡面的原因

  一配置檔案

<!-- 配置sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 例項化sqlSessionFactory時需要使用上述配置好的資料來源以及SQL對映檔案 -->
        <property name="dataSource" ref="dataSource" />
        <!-- 自動掃描me/gacl/mapping/目錄下的所有SQL對映的xml檔案, 省掉Configuration.xml裡的手工配置
        value
="classpath:me/gacl/mapping/*.xml"指的是classpath(類路徑)下me.gacl.mapping包中的所有xml檔案 UserMapper.xml位於me.gacl.mapping包下,這樣UserMapper.xml就可以被自動掃描 --> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="mapperLocations" value="classpath:me/gacl/mapping/*.xml" /> </bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 掃描me.gacl.dao這個包以及它的子包下的所有對映介面類 -->
        <property name="basePackage" value="me.gacl.dao" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>

  接下來我們就好好分析這兩個類SqlSessionFactoryBean ,MapperScannerConfigurer

  二SqlSessionFactoryBean

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

  它是一個FactoryBean,那我們只需要關注getObject方法就好了

@Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

  這樣,spring容器內就有一個java bean,name是我們配的sqlSessionFactory

  三MapperScannerConfigurer

  它實現了介面BeanDefinitionRegistryPostProcessor 就說明它具有想beanFactory註冊BeanDefinition的能力

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware

  

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();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

  因為我們只配了<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> 所以這裡 this.sqlSessionFactory = null

  basePackage =me.gacl.dao

  直接跳到ClassPathMapperScanner.doScan

  

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
      for (BeanDefinitionHolder holder : beanDefinitions) {
        GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();

        if (logger.isDebugEnabled()) {
          logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
              + "' and '" + definition.getBeanClassName() + "' mapperInterface");
        }

        // the mapper interface is the original class of the bean
        // but, the actual class of the bean is MapperFactoryBean
        definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
        definition.setBeanClass(MapperFactoryBean.class);

        definition.getPropertyValues().add("addToConfig", this.addToConfig);

        boolean explicitFactoryUsed = false;
        if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
          definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
          explicitFactoryUsed = true;
        } else if (this.sqlSessionFactory != null) {
          definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
          explicitFactoryUsed = true;
        }

  一個BeanDefinition就這樣被完成了,並註冊到beanFactory裡。它有幾個重要的屬性

  1 該Bean的class是MapperFactoryBean

  2 它有屬性mapperInterface 這裡是me.gacl.dao.UserMapper

  3 它有屬性sqlSessionFactory 就是在上一小節得到的sqlSessionFactory (Mybatis的原生類)

  好了,到這裡後我們要分析的程式碼就是MapperFactoryBean

  四MapperFactoryBean

  這個類不得了,可以說不能執行Batch的原因就出在他身上

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  private Class<T> mapperInterface;

  private boolean addToConfig = true;

  可以看出來MapperFactoryBean 本身就一個屬性mapperInterface 表示的是 me.gacl.dao.UserMapper

  主要的功能和屬性都在SqlSessionDaoSupport

  而且它本身就是一個FactoryBean 還是來看