前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >mybatis mapper解析(下) @mapperScan

mybatis mapper解析(下) @mapperScan

作者头像
平凡的学生族
发布2019-05-29 08:52:40
5.3K0
发布2019-05-29 08:52:40
举报
文章被收录于专栏:后端技术后端技术

参考

深入剖析 mybatis 原理(三)如何整合Spring 没有他的参考,我不可能做出这篇理解。

@mapperScan注册MapperFactoryBean

@MapperScan.jpg

注册的那些MapperFactoryBean(是一种FactoryBean)会在spring初始化时调用其getObject方法生成具体Bean。(关于FactoryBean,可参考Spring源码学习--FactoryBean实现原理)

MapperFactoryBean初始化

mapperFactory初始化.jpg

但在MapperFactoryBean实例化后,调用getObject之前,由于MapperFactoryBean本身也是个Bean,spring框架会调用setSqlSessionFactory和setSqlSessionTemplate设置其属性。以便之后的getObject使用。

代码语言:javascript
复制
// SqlSessionDaoSupport.java
// MapperFactoryBean继承了SqlSessionDaoSupport

// 设置sqlSessionFactory, 顺便自动设置sqlSessionTemplate。所以如果没设定sqlSessionTemplate也没关系。
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
      this.sqlSessionTemplate = createSqlSessionTemplate(sqlSessionFactory);
    }
}

// 可以手动设置
public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
    this.sqlSessionTemplate = sqlSessionTemplate;
}

在之后的getObject中:

  1. getSqlSession就用到了刚才设置的sqlSessionTemplate。
  2. getMapper(this.mapperInterface)深入剖析 mybatis 原理(二)已经分析过了 ,该方法最终会返回Mapper代理类对象。
代码语言:javascript
复制
// MapperFactoryBean.java
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }
代码语言:javascript
复制
// SqlSessionDaoSupport.java
  public SqlSession getSqlSession() {
    return this.sqlSessionTemplate;
  }

getObject的调用

你可能会问,那这个MapperFactoryBean.getObject何时会调用呢? 还记得@mapperScan注册MapperFactoryBean时的ClassPathMapperScanner.processBeanDefinitions方法吗:

代码语言:javascript
复制
  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
      definition.setBeanClass(this.mapperFactoryBeanClass);
      // ...
      }
    }
  }

用途有两个:

  1. 这里为Bean注册的名字beanClassName是xx.xx.xxMapper,但实际的Bean是MapperFactoryBean。 在springboot初始化的过程中,会对每个Bean调用getBean进行实例化,因此MapperFactoryBean.getObject也会被调用。
  2. Service初始化时,spring会处理@Autowire中的Mapper,根据其类型的名字xx.xx.xxMapper找Bean,然后调用beanFactory.getBean(beanName);。但根据这个名字找到的其实是刚才注册的MapperFactoryBean。由于它是FactoryBean,对其调用getBean只会触发getObject,从而返回代理类对象。 要验证@Autowired在Mapper上的作用,请自行调试,可参考后文。

调试@autowired的Mapper的注入

  1. AutowiredAnnotationBeanPostProcessor.inject中的Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);打断点调试。
  2. 当变量beanName名字是包含了Mapper的Service或Controller时,进入该方法。 然后跟随进入result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);->instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);->beanFactory.getBean(beanName);。进入调试。熟悉BeanFactory.getBean的人应该不用调试就知道,这个名字对应的Bean是MapperBeanFactory,也就是一个BeanFactory,所以最终该方法beanFactory.getBean(beanName);返回的是MapperFactoryBean的getObject的返回值。这个getObject就是刚才分析的用途啦!

总结

  1. @MapperScan扫描指定的包,对每个Mapper,以它的名字注册了实际类型是MapperFactoryBean的Bean定义。
  2. 有了这些Bean定义,在spring实例化Bean时,这些MapperFactoryBean会被实例化、初始化,对应的方法也会设置
  3. 在处理@autowired标注的Mapper时,会返回MapperFactoryBean.getObject的调用结果,也就是getSqlSession().getMapper(this.mapperInterface);了。
  4. 上一步的结果会导致@Autowired SomeMapper mapper;上注入了一个Mapper代理类,该代理类会将所有数据库请求都移交给底层的SqlSession操作。
  5. 上一步中,Mapper移交到的sqlSession其实是个SqlSessionTemplate,SqlSessionTemplate又将一切数据库操作移交给sqlSessionProxy,而后者是基于SqlSessionInterceptor创建的代理类。 也就是说,SqlSessionTemplate的数据库操作会被SqlSessionInterceptor.invoke所拦截。
  6. SqlSessionInterceptor.invoke中调用getSqlSession方法,其内部在需要的时候调用session = sessionFactory.openSession(executorType);获取新的session,其实也就在开启新的连接。 也就是说SqlSessionTemplate的数据库操作会被SqlSessionInterceptor.invoke所拦截,每次操作前都要获取到SqlSession(实际类型是DefaultSqlSession),这个SqlSession:
    • 要么是复用现有的(比如复用当前事务使用的)
    • 要么是新建的。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.05.29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 参考
  • @mapperScan注册MapperFactoryBean
  • MapperFactoryBean初始化
  • getObject的调用
  • 调试@autowired的Mapper的注入
  • 总结
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档