mybatis(2) - MapperScan到底做了什么

本来只是因为【sqlSessionTemplate重复注入找解决方法】,在排查过程中又对ClassPathMapperScanner 到底干了什么产生了疑问,为什么是MapperFactoryBean构建Mapper ? 为什么会有多次处理sqlSessionTemplete?

结论

  1. ClassPathMapperScanner 
    1. 决定了对于mapper对象使用mapperFactoryBean来wrapper,并确定了mapperFactoryBean需要注入的属性值对PropertyValues。
    2. ClassPathMapperScanner 控制了是否需要设置预设的 sqlSessionFactory、sqlSessionTemplate,可以用做多数据源切换的一种实现方案(另外一种:AbstractRoutingDataSource)。
      如果设置了,AutowireMode默认是AUTOWIRE_NO ;否则是 AUTOWIRE_BY_TYPE;
  2. 在AbstractAutowireCapableBeanFactory的populateBean方法中将判定AUTOWIRE_BY_TYPE
    1. : 执行autowireByType方法 。根据类的成员变量属性描述及setXXXX来确定mapperFactoryBean需要注入哪些实例,如果容器中存在或者可以创建这个实例,那就会注入!
    2. : 跳过autowireByType方法。 直接按在ClassPathMapperScanner 确定的PropertyValues 直接设置mapperFactoryBean属性值。
  3. 各种MapperScan的类继承的接口入参中都包含BeanDefinitionRegistry , 将在容器初始化过程中被注入! 实际上DefaultListableBeanFactory implements BeanDefinitionRegistry 保存bean的定义信息。

ClassPathMapperScanner

主要功能是扫描出路径下的Mapper对象被将其将BeanDefinition保存在全局的BeanDefinitionRegistry (DefaultListableBeanFactory)中。

从上篇博客章节【@Mapper与@MapperScan如何正确使用】中了解到:
MapperScannerConfigurer、@MapperScan import的MapperScannerRegistrar、自动扫描@MapperAutoConfiguredMapperScannerRegistrar都是创建了ClassPathMapperScanner(默认路径为启动Applicaiton类的包路径)。

下文是MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor

b4032698e5c6ff23f78332498ecd70bc75e.jpg

源码分析

64323a22a7b413ea232ec249f5a02092895.jpg

发现上篇文章问题中说到的SqlSessionFactory、SqlSessionTemplate、MapperFactoryBean都出现了。

doScan方法

30b11f9ca9350408966ba66f114379e58eb.jpg

在断点过程中发现:
doScan方法在通过basePackage执行findCandidateComponens方法时使用了ClassPathMapperScannerisCandidateComponent方法对返回结果集判定时: 只是判定bean的定义描述是否是接口

bfcfc63025d548fd273bbbfa0ad7d5e326f.jpg

所以如果指定的扫描路径basePackage含有其他接口类的话,doScan最终返回的BeanDefinitionHolder集合中会含有很多不相干的接口信息。
bacbf9f46b739688d13f56f377da93a5255.jpg

最后:

调用BeanDefinitionRegistry的registerBeanDefinition方法将这个BeanDefinition保存在DefaultListableBeanFactory(implements BeanDefinitionRegistry)的Map<String, BeanDefinition> beanDefinitionMap 中!
f8d95be8769313c63e6fa635fb1d9579aa4.jpg

ebbc9588813f6124f5ea975d584d7b5323f.jpg

processBeanDefinitions方法

填充BeanDefinition的属性内容。

  1. 设置beanClass为mapperFactoryBean。(这也证实了对于每一个Mapper都会通过mapperFactoryBean来构建
    5522fd3783e7f91e5697bd6c8260946c9f6.jpg
  2. 判定是否需要设置 sqlSessionFactory、sqlSessionTemplate;  如果有设置的话explicitFactoryUsed = true.(此时测试用例的执行都没有设置这些值, 如果想使用多数据源,可以在MapperScan时指定扫描Dao路径的同时增加对sqlSessionFactory的指定
    0ddcccf509ba028ef88219aaa834e2d08b7.jpg
  3. 设置自动注入的类型AutowireMode!很关键!而且:如果有设置sqlSessionFactory/sqlSessionTemplate 则不设置AutowireMode, 默认是AUTOWIRE_NO!(控制是否自动注入)
    053de617f2fc55be32397d0df4f255dbaa4.jpg

最终的MutablePropertyValues属性值

66f0f5dd367dd3c9eae7c0891bfb27ff0bf.jpg

AbstractAutowireCapableBeanFactory

那问题就来了: 为什么在上篇博客中, createBean时 AbstractAutowireCapableBeanFactory.populateBean方法最终处理得到的属性多了sqlSessionFactory、sqlSessionTemplate?
919cf127c49e7f1810541f31be6b8c00422.jpg

RootBeanDefinition在进入populateBean方法时确实还保持着和MapperScan时一致的属性集合。

222d368f5c327e53ad0d906e384e47fed35.jpg

注意:因为在扫描注册时设置了AUTOWIRE_BY_TYPE,所以会执行autowireByType方法

85ce58598dee6fbf30d292062d70f0964e6.jpg

在autowireByType方法中通过unsatisfiedNonSimpleProperties方法来获得需要注入的属性字段

f0e8e13f3f6ff7f345fb834762f880a957e.jpg

unsatisfiedNonSimpleProperties方法

58d6f06a8e2d5ff37de500e45c2670c2862.jpg

在方法进入时PropertyValues还是与扫描时一致。关键在于:PropertyDescriptor[]中有MapperFactoryBean的所有成员属性的描述信息包括sqlSessionTemplate、sqlSessionFactory!

56458d2838c8b8c5fd58bb18e6c14adf944.jpg

在轮询PropertyDescriptor[] 时判定getWriteMethod()时: 判定有setSqlSessionFactory()和 setSqlSessionTemplate() 。

389a4e5240651e8468e071ce8616be4b92a.jpg

3c151946c5d5e9fda7a6a53edf01b8190ac.jpg

所以返回的结果集包涵了两者!!
6cf95fbde153381179ed2088301d264327d.jpg

接着又回到了autowireByType方法的执行。

autowireByType 方法

核心是: 通过处理好的propertyNames来注入真实实例

921d082c0efd431b0710504b88d935856f9.jpg

跟踪断点发现:

获取到了容器中已有的defaultSqlSessionFactory的实例。判定不等于空填入MutablePropertyValues集合中

266d995af57feb2dd19c6b6fe1a046357dc.jpg

ecd655d76bf526392591f499cdae98ea608.jpg

无法获取SqlSessionTemplate实例就不置入MutablePropertyValues集合中

a03e3dfac891576e243e600d645781a56a6.jpg

95169adcbd8032e9e9f854d427ca6057420.jpg

所以: 最终回到populateBean时就确定好了PropertyValues!此时将只会执行setSqlSessionFactory!
56be53661a208fe69a23bba7f90d45b7a2c.jpg

反向验证

 测试时增入自己显示声明 @Bean SqlSessionTemplate:

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

断点发现它又有实例了!所以 MapperFactoryBean  先setSqlSessionFactory再 setSqlSessionTemplate~

ab22f100a5ca2caae923c550c54097f00fc.jpg

96057877d9adb246dccba5e138a05a9c3f3.jpg

其他关键源码

 MapperScannerRegistrar implements ImportBeanDefinitionRegistrar

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    // this check is needed in Spring 3.1
    if (resourceLoader != null) {
      scanner.setResourceLoader(resourceLoader);
    }

    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      scanner.setAnnotationClass(annotationClass);
    }

    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      scanner.setMarkerInterface(markerInterface);
    }

    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
    }

    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }

    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

    List<String> basePackages = new ArrayList<String>();
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }

 AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar:

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

      logger.debug("Searching for mappers annotated with @Mapper");

      ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

      try {
        if (this.resourceLoader != null) {
          scanner.setResourceLoader(this.resourceLoader);
        }

        List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
        if (logger.isDebugEnabled()) {
          for (String pkg : packages) {
            logger.debug("Using auto-configuration base package '{}'", pkg);
          }
        }

        scanner.setAnnotationClass(Mapper.class);
        scanner.registerFilters();
        scanner.doScan(StringUtils.toStringArray(packages));
      } catch (IllegalStateException ex) {
        logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
      }
    }

 

转载于:https://my.oschina.net/u/3434392/blog/3010046

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值