mybatis(2) - MapperScan 注入過程

原本只是由於【sqlSessionTemplate重複注入找解決方法】,在排查過程當中又對ClassPathMapperScanner 到底幹了什麼產生了疑問,爲何是MapperFactoryBean構建Mapper ? 爲何會有屢次處理sqlSessionTemplete?java

結論

  1. ClassPathMapperScanner processBeanDefinitions方法中
    1. 決定了對於mapper對象使用mapperFactoryBean來wrapper,並肯定了mapperFactoryBean須要注入的屬性值對PropertyValues。
    2. 控制了是否須要設置預設的 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)中。sql

從上篇博客章節【@Mapper與@MapperScan如何正確使用】中瞭解到:
MapperScannerConfigurer、@MapperScan import的MapperScannerRegistrar、自動掃描@Mapper的AutoConfiguredMapperScannerRegistrar都是建立了ClassPathMapperScanner(默認路徑爲啓動Applicaiton類的包路徑)。app

下文是MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor源碼分析

源碼分析

發現上篇文章問題中說到的SqlSessionFactory、SqlSessionTemplate、MapperFactoryBean都出現了。測試

doScan方法

debug過程當中發現:
doScan方法在經過basePackage執行findCandidateComponens方法時
使用了ClassPathMapperScanner本身的isCandidateComponent方法對返回結果集斷定時: 只是斷定bean的定義描述是不是接口this

若是指定的掃描路徑basePackage含有其餘接口類的話,doScan最終返回的Set<BeanDefinitionHolder>中會含有不少不相干的接口信息。
spa

因此:.net

  1. 掃描路徑basePackage精確到DAO層;
  2. 或者在指定@MapperScan時,額外指定"markerInterface" 明確接口類型 或"annotationClass"明確註解類型。
    通常在doScan以前調用ClassPathMapperScannerregisterFilters()方法依此來構建AnnotationTypeFilterAssignableTypeFilterfindCandidateComponents 時還是調用 scanCandidateComponents 處理。filters在isCandidateComponent(MetadataReader metadataReader) 生效(常規流程)

最終:debug

調用BeanDefinitionRegistry的registerBeanDefinition方法將這個BeanDefinition保存在DefaultListableBeanFactory(implements BeanDefinitionRegistry)的Map<String, BeanDefinition> beanDefinitionMap 中!
3d

processBeanDefinitions方法

填充BeanDefinition的屬性內容。

  1. 設置beanClass爲mapperFactoryBean。(這也證明了對於每個Mapper都會經過mapperFactoryBean來構建
  2. 斷定是否須要設置 sqlSessionFactory、sqlSessionTemplate;  若是有設置的話explicitFactoryUsed = true.(此時測試用例的執行都沒有設置這些值, 若是想使用多數據源,能夠在MapperScan時指定掃描Dao路徑的同時增長對sqlSessionFactory的指定
  3. 設置自動注入的類型AutowireMode!很關鍵!並且:若是有設置sqlSessionFactory/sqlSessionTemplate 則不設置AutowireMode, 默認是AUTOWIRE_NO!(控制是否自動注入)

最終的MutablePropertyValues屬性值

AbstractAutowireCapableBeanFactory

那問題就來了: 爲何在上篇博客中, createBean時 AbstractAutowireCapableBeanFactory.populateBean方法最終處理獲得的屬性多了sqlSessionFactory、sqlSessionTemplate?

RootBeanDefinition在進入populateBean方法時確實還保持着和MapperScan時一致的屬性集合。

注意:由於在掃描註冊時設置了AUTOWIRE_BY_TYPE,因此會執行autowireByType方法

在autowireByType方法中經過unsatisfiedNonSimpleProperties方法來得到須要注入的屬性字段

unsatisfiedNonSimpleProperties方法

在方法進入時PropertyValues仍是與掃描時一致。關鍵在於:PropertyDescriptor[]中有MapperFactoryBean的全部成員屬性的描述信息包括sqlSessionTemplate、sqlSessionFactory!

在輪詢PropertyDescriptor[] 時斷定getWriteMethod()時: 斷定有setSqlSessionFactory()和 setSqlSessionTemplate() 。

因此返回的結果集包涵了二者!!

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

autowireByType 方法

核心是: 經過處理好的propertyNames來注入真實實例

跟蹤斷點發現:

獲取到了容器中已有的defaultSqlSessionFactory的實例。斷定不等於空填入MutablePropertyValues集合中

沒法獲取SqlSessionTemplate實例就不置入MutablePropertyValues集合中

因此: 最終回到populateBean時就肯定好了PropertyValues!此時將只會執行setSqlSessionFactory!

反向驗證

 測試時增入本身顯示聲明 @Bean SqlSessionTemplate:

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

斷點發現它又有實例了!因此 MapperFactoryBean  先setSqlSessionFactory再 setSqlSessionTemplate~

其餘關鍵源碼

 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);
      }
    }
相關文章
相關標籤/搜索