Mybatis整合到spring原理

Mybatis整合到spring

SqlSessionTemplate整合

spring提供SqlSessionTemplate类,用于在spring中注入该类实例进行sql操作。该类实现了SqlSession接口,直接调用sqlSession的方法用于执行sql,该类持有SqlSessionFactory类,每次执行方法都会创建新的sqlSession。

  • SqlSessionTemplate类结构如下图。其中SqlSessionInterceptor是内部动态代理类,sqlSessionProxy代理对象执行的是内部类SqlSessionInterceptor的invoke方法。
    Mybatis整合到spring原理
  • sqlSessionProxy是实现了SqlSession接口的动态代理类,SqlSessionTemplate的selectOne等方法执行的实际是代理类的invoke方法
this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
  • 代理类SqlSessionInterceptor每次执行invoke时都会生成新的SqlSession对象。就是通过这里保证每次sqlSession线程安全的。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//每次获取新的sqlSession
   SqlSession sqlSession = 
   SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
	...
}
  • 在使用时,需要每次都注入SqlSessionTemplate,为了方便,可以通过继承SqlSessionDaoSupport类。该类持有SqlSessionTemplate实例。

MapperScanConfiguration自动注入整合

通过上述SqlSessionTemplate方式还是不方便,可以通过定义Mapper接口,直接用@Autowired方式注入后直接使用。通过这个方式是怎么得到代理对象,怎么执行对应的sql的呢?

  • MapperScanConfiguration类实现了BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,在方法中扫描了Mapper类,封装成BeanDefinition,得到所有Mapper的BeanDefinition后执行BeanDefinition的setBeanClass方法,把beanClass修改为MapperFacotryBean.class,所以最终注册在BeanDefinition容器中的是MapperFacotryBean类。每个MapperFactoryBean还持有对应的Mapper类。这一步可以把无法实例化的Mapper接口变为可以实例化的MapperFactoryBean,也把对应的Mapper与后续创建的代理对象关联起来了。
//执行路径postProcessBeanDefinitionRegistry#ClassPathMapperScanner#doScan#processBeanDefinitions

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
          + "' mapperInterface");

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
//把获得的对应的Mapper接口类的BeanDefinition的类类型修改为MapperFactoryBean类型。
      definition.setBeanClass(this.mapperFactoryBeanClass);

      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;
      }

      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        if (explicitFactoryUsed) {
          LOGGER.warn(
              () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate",
            new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionTemplate != null) {
        if (explicitFactoryUsed) {
          LOGGER.warn(
              () -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
        explicitFactoryUsed = true;
      }

      if (!explicitFactoryUsed) {
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
      definition.setLazyInit(lazyInitialization);
    }
  }
  • MapperFactoryBean实现了FactoryBean接口,所以在spring容器实例化阶段,实例化的bean是从getObject方法返回的对象。MapperFactoryBean继承了SqlSessionDaoSupport,可以拿到SqlSession,参看前面的Mybatis执行流程可以得到Mapper的代理对象,所以注册到IoC容器的其实是MapperProxy生成的Mapper代理对象。
 @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
  • 在使用时,spring会注入对应的Mapper的代理对象,执行MapperProxy的invoke方法对sq进行操作。
上一篇:Mybatis3基础-工作原理(不整合mybatis-spring)


下一篇:MyBatis执行流程 助眠神器 睡不着建议看