Spring整合MyBatis原理-MapperFactoryBean

在上一篇《Spring整合MyBatis原理-@MapperScan》中,我们知道mybatis-spring框架的@MapperScan会将扫描包下的所有接口定义成一个MapperFactoryBean类型的BeanDefinition注册到IoC容器中。那么今天我们就来研究一下MapperFactoryBean

基本结构

Spring整合MyBatis原理-MapperFactoryBean
image.png
public class MapperFactoryBean<Textends SqlSessionDaoSupport implements FactoryBean<T{
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
}

从类图中可以看到,MapperFactoryBean是一个FactoryBean,因此其主要逻辑就在getObject中。

MapperFactoryBean#getObject()

  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
  1. getSqlSession():这个方法在其父类SqlSessionDaoSupport中实现,返回一个SqlSessionTemplate
  2. 调用SqlSessionTemplate#getMapper

SqlSessionDaoSupport#getSqlSession

  public SqlSession getSqlSession() {
    return this.sqlSessionTemplate;
  }

这个方法没多大意义,直接返回属性,但是这个属性何时被赋值呢?

属性何时被赋值的问题,我们要回到MapperFactoryBean如何定义的代码看一下了

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (AbstractBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();
      definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName));
      definition.setBeanClass(this.mapperFactoryBeanClass);
      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);
    }
  }   
}

上面这段代码是ClassPathMapperScanner扫描完所有的接口,然后将接口的BeanDefinition注册 可以看到在注册MapperFactoryBean时,会设置SqlSessionFactorySqlSessionTemplate若都没设置,也会通过byType的自动装配 那大家有疑问了?如果我只设置SqlSessionFactorySqlSessionTemplate还有值吗?答案是有的,即使只设置SqlSessionFactorySqlSessionTemplate也会在设置SqlSessionFactory时被自动创建。 下面我们一起看下这段代码

public abstract class SqlSessionDaoSupport extends DaoSupport {

  private SqlSessionTemplate sqlSessionTemplate;

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
      this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
    }
  }

}

大家需要清楚一点,通过BeanDefinition中的PropertyValues配置属性和byType的自动装配。Spring在填充属性时会走其setXxx方法,那么我们看下setSqlSessionFactory如何实现 它会调用createSqlSessionTemplate

protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    return new SqlSessionTemplate(sqlSessionFactory);
}

大家看到这里是不是略懂了,若只设置SqlSessionFactorySqlSessionTemplate也会自动的被创建

SqlSessionTemplate#getMapper

public class MapperFactoryBean<Textends SqlSessionDaoSupport implements FactoryBean<T{

  private Class<T> mapperInterface;
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
}

调用SqlSessionTemplate#getMapper方法,其入参是mapperInterface就是XxxxMapper.class接口类型

public class SqlSessionTemplate implements SqlSessionDisposableBean {
  public <T> getMapper(Class<T> type) {
    return getConfiguration().getMapper(type, this);
  }  
  @Override
  public Configuration getConfiguration() {
    return this.sqlSessionFactory.getConfiguration();
  }
}

最终从SqlSessionFactoryConfiguration中获取Mapper. 还要注意一点获取的Mapper传的SqlSession是this,即SqlSessionTemplate

public <T> getMapper(Class<T> type, SqlSession sqlSession) {
    return this.mapperRegistry.getMapper(type, sqlSession);
}

小总结

MyBatis会将Mapper接口生成代理对象存放到mapperRegistry,这段逻辑属于MyBatis框架原理的内容。 通过上文中的代码解读,我们通过Spring框架获取的Mapper接口,内部都拥有一个SqlSessionTemplate

Spring整合MyBatis原理-MapperFactoryBean
image.png

mybatis-spring通过SqlSessionTemplate获取SqlSessionFactory,从SqlSessionFactory获取Configuration,从Configuration中的mapperRegistry获取到Mapper代理对象Mapper代理对象中再设置SqlSessionTemplate我们注入的Mapper接口,就是Mapper接口的代理对象(是由MyBatis框架生产的代理对象)

总结

这篇文章简单说了一下MapperFactoryBean的工作原理。主要记住几个关键点

  • MapperFactoryBean会自动用SqlSessionFactory创建SqlSessionTemplate
  • MapperFactoryBean#getObject返回Mapper代理对象
  • Mapper代理对象中拥有SqlSessionTemplate
  • 我们注入Mapper接口时,注入的就是接口对应的Mapper代理对象


原文始发于微信公众号(溪溪技术笔记):Spring整合MyBatis原理-MapperFactoryBean

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/207123.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!