mybatis與spring整合源碼分析

1.配置applicationContext.xml

	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://122.152.131.141:3306/school" />
		<property name="username" value="zw121" />
		<property name="password" value="122334" />
	</bean>

	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:mybatis.xml" />
		<property name="mapperLocations">
			<array>
				<!-- 配置單個映射文件 -->
				<!-- <value>classpath:mappers/UserMapper.xml</value> -->
				<!-- 配置多個映射文件使用 * 通配符 -->
				<value>classpath:mappers/*Mapper.xml</value>
			</array>
		</property>
		<!-- 配置別名,使用包掃描 -->
		<property name="typeAliasesPackage" value="com.qhf.pojo"></property>
	</bean>
	
	<!-- 掃描接口文件,其2選1 -->
	<!-- 1.掃描接口文件,只能單個文件 -->
	<!-- 
	<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="mapperInterface" value="com.qhf.dao.UserMapper"></property>
		<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
	</bean>
	 -->
	<!-- 2.掃描包下的接口文件,此方法簡便 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.qhf.dao"></property>
	</bean>
  •  主要就是配置 SqlSessionFactoryBean 的初始化 bean 對象
  • 還有 MapperFactoryBean 的初始化 bean 對象,此 bean 針對一個 mapper 接口
  • MapperScannerConfigurer 的初始化 bean 對象,掃描包下的全部接口文件

 

 

 2.SqlSessionFactoryBean

  • 實現接口 FactoryBean 接口,getBean() 方法返回 sqlSessionFactory 工廠類對象。
  • 實現 InitializingBean 接口,則 bean 初始化時會調用此類的 afterPropertiesSet() 方法。
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> { 
  //...
  @Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

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

    return this.sqlSessionFactory;
  }
}
  •  sqlSessionFactory 的初始化實際上就是調用 buildSqlSessionFactory() 方法時初始化的。
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;

    XMLConfigBuilder xmlConfigBuilder = null;
    if (this.configLocation != null) {
      xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
      configuration = xmlConfigBuilder.getConfiguration();
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
      }
      configuration = new Configuration();
      configuration.setVariables(this.configurationProperties);
    }

    if (this.objectFactory != null) {
      configuration.setObjectFactory(this.objectFactory);
    }

    if (this.objectWrapperFactory != null) {
      configuration.setObjectWrapperFactory(this.objectWrapperFactory);
    }

    if (hasLength(this.typeAliasesPackage)) {
      String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeAliasPackageArray) {
        configuration.getTypeAliasRegistry().registerAliases(packageToScan,
                typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
        }
      }
    }

    if (!isEmpty(this.typeAliases)) {
      for (Class<?> typeAlias : this.typeAliases) {
        configuration.getTypeAliasRegistry().registerAlias(typeAlias);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Registered type alias: '" + typeAlias + "'");
        }
      }
    }

    if (!isEmpty(this.plugins)) {
      for (Interceptor plugin : this.plugins) {
        configuration.addInterceptor(plugin);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Registered plugin: '" + plugin + "'");
        }
      }
    }

    if (hasLength(this.typeHandlersPackage)) {
      String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      for (String packageToScan : typeHandlersPackageArray) {
        configuration.getTypeHandlerRegistry().register(packageToScan);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
        }
      }
    }

    if (!isEmpty(this.typeHandlers)) {
      for (TypeHandler<?> typeHandler : this.typeHandlers) {
        configuration.getTypeHandlerRegistry().register(typeHandler);
        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Registered type handler: '" + typeHandler + "'");
        }
      }
    }

    if (xmlConfigBuilder != null) {
      try {
        xmlConfigBuilder.parse();

        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
        }
      } catch (Exception ex) {
        throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
      } finally {
        ErrorContext.instance().reset();
      }
    }

    if (this.transactionFactory == null) {
      this.transactionFactory = new SpringManagedTransactionFactory();
    }

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

    if (this.databaseIdProvider != null) {
      try {
        configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
      } catch (SQLException e) {
        throw new NestedIOException("Failed getting a databaseId", e);
      }
    }

    if (!isEmpty(this.mapperLocations)) {
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }

        try {
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
              configuration, mapperLocation.toString(), configuration.getSqlFragments());
          xmlMapperBuilder.parse();
        } catch (Exception e) {
          throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
        } finally {
          ErrorContext.instance().reset();
        }

        if (LOGGER.isDebugEnabled()) {
          LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
        }
      }
    } else {
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
      }
    }

    return this.sqlSessionFactoryBuilder.build(configuration);
  }
  • 總體來說:
    • 生成一個 Configuration 對象,此對象是 mybatis 的核心。
    • 把配置的 configLocation(mybatis原始配置文件,整合之後基本不用配置),objectFactory,objectWrapperFactory,typeAliasesPackage(別名,省了configLocation 裏別名的配置),typeAliases,plugins,typeHandlersPackage,typeHandlers,transactionFactory,databaseIdProvider,mapperLocations(掃描 xml 映射文件) 初始化和解析
    • 最後通過 sqlSessionFactoryBuilder.build(configuration) 方法把此 configuration 對象傳給了 DefaultSqlSessionFactory 實例對象,其中 DefaultSqlSessionFactory 實現了 SqlSessionFactory 接口,是生產 SqlSession 的工廠。
public class SqlSessionFactoryBuilder {
  //...
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
}


public class DefaultSqlSessionFactory implements SqlSessionFactory {

  private final Configuration configuration;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }
  //...
}
  • 當單獨使用 mybatis 時通過 sqlSessionFactory.openSession() 來獲取 sqlSession,才能操作數據庫。

SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//...

  • 當交給 Spring 管理,可以通過下面方式獲取 userMapper 對象

UserMapper userMapper = (UserMapper)context.getBean("userMapper");

@Autowired

UserMapper userMapper;

  • 先看此配置,是對單個 mapper 接口的 bean 對象的創建。
	<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="mapperInterface" value="com.qhf.dao.UserMapper"></property>
		<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
	</bean>

 

 

3.MapperFactoryBean

  • 配置 MapperFactoryBean 的 bean 時的屬性是 mapperInterface,sqlSessionFactory

    <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="com.qhf.dao.UserMapper"></property>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
    </bean>

  • <property name="sqlSessionFactory" ref="sqlSessionFactory"/>屬性初始化時會調用 MapperFactoryBean 從父類 SqlSessionDaoSupport 中繼承下來的 setSqlSessionFactory() 方法,並使用 SqlSessionTemplate 對 sqlSessionFactory 進行加工生產 sqlSession(通過動態代理)
  •   public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        if (!this.externalSqlSession) {
          this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
        }
      }
  • MapperFactoryBean 實現了 InitializingBean 接口,則 bean 初始化時會調用此類的 afterPropertiesSet() 方法,此方法在其父類中。
public abstract class DaoSupport implements InitializingBean {
    @Override
	public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
		// Let abstract subclasses check their configuration.
		checkDaoConfig();

		// Let concrete implementations initialize themselves.
		try {
			initDao();
		}
		catch (Exception ex) {
			throw new BeanInitializationException("Initialization of DAO failed", ex);
		}
	}
}
  • checkDaoConfig() 方法在 DaoSupport 的子類 MapperFactoryBean 中實現。
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
  @Override
  protected void checkDaoConfig() {
    super.checkDaoConfig(); // notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");

    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        configuration.addMapper(this.mapperInterface);
      } catch (Exception e) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
        throw new IllegalArgumentException(e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }
}
  • getSession() 方法返回 this.sqlSession,getConfiguration() 返回 Configuration 對象
  • 再檢查 Configuration 對象中是否已配置了 <property name="mapperInterface" value="com.qhf.dao.UserMapper"/> 此mapper 接口(因爲手動配置,所以還是要檢查。重點是 mybatis 中讀取映射文件時解析到<mapper namespace="xxx.UserMapper">時會自動進行類型的註冊)
  • 如果沒有的話便會通過 configuration.addMapper(this.mapperInterface) 方法進行 mapper 接口的註冊
  • 回到1中最後的問題:UserMapper userMapper = (UserMapper)context.getBean("userMapper"); 爲什麼能自動返回了 userMapper 對象?MapperFactoryBean 實現 FactoryBean 接口,重寫了其接口方法來獲取 mapper 對象。
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {  
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
}

 

 

4.MapperScannerConfigurer

  • 同樣是繼承 InitializingBean,但是此方法沒作用;重點在父接口 BeanDefinitionRegistryPostProcessor,BeanDefinitionRegistryPostProcessor實現了BeanFactoryPostProcessor接口,是Spring框架的BeanDefinitionRegistry的後處理器,用來註冊額外的BeanDefinition。postProcessBeanDefinitionRegistry方法會在所有的BeanDefinition已經被加載了,但所有Bean還沒有被創建前調用。BeanDefinitionRegistryPostProcessor經常被用來註冊BeanFactoryPostProcessor的BeanDefinition。MapperScannerConfigurer 重寫了其 postProcessBeanDefinitionRegistry() 方法。
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
  @Override
  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));
  }
}
  • 後續加上

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章