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));
  }
}
  • 后续加上

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值