1. 程式人生 > >二、MyBatis Mapper Bean初始化深度解析5-4

二、MyBatis Mapper Bean初始化深度解析5-4

開發十年,就只剩下這套架構體系了! >>>   

四、MapperScannerRegistrar載入mapper bean

在ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsForConfigurationClass方法中,會從bean註冊器中載入bean:

這裡就包括了上一步被載入進來的MapperScannerRegistrar。loadBeanDefinitionsFromRegistrars的實現如下:

	private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
		registrars.forEach((registrar, metadata) ->
				registrar.registerBeanDefinitions(metadata, this.registry));
	}

這裡會去呼叫實現類的registerBeanDefinitions方法去載入bean。前面說過,MapperScannerRegistrar實現了介面ImportBeanDefinitionRegistrar,具有方法:registerBeanDefinitions。因此,載入mapper bean就正式開始了。

MapperScannerRegistrar的registerBeanDefinitions方法實現如下:

 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(mapperScanAttrs, registry);
    }
  }

上述程式碼會去讀取註解@MapperScan的值,雖然@MapperScan有很多屬性可以配置,這裡,我們只配置了value屬性,其值為:

@MapperScan(value = {"com.iwill.mybatis.dao.mapper.ext", "com.iwill.mybatis.dao.mapper.gen"})

方法registerBeanDefinitions裡面會設定掃描器(掃描器為ClassPathMapperScanner)以及配置讀取類的包路徑和註冊掃描規則:

 basePackages.addAll(
        Arrays.stream(annoAttrs.getStringArray("value"))
            .filter(StringUtils::hasText)
            .collect(Collectors.toList()));

    basePackages.addAll(
        Arrays.stream(annoAttrs.getStringArray("basePackages"))
            .filter(StringUtils::hasText)
            .collect(Collectors.toList()));

    basePackages.addAll(
        Arrays.stream(annoAttrs.getClassArray("basePackageClasses"))
            .map(ClassUtils::getPackageName)
            .collect(Collectors.toList()));

    scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(basePackages));

也即是說:註解@MapperScan的value、basePackages、basePackageClasses等屬性配置的包路徑都會被掃描。

配置的掃描過濾器(掃描規則scanner.registerFilters())如下:

我們沒有在@MapperScan裡面指定需要掃描的標記指定註解的類(annotationClass屬性來指定),所以acceptAllInterfaces會為true,即會掃描包路徑下的所有介面,但是package-info結尾的去掉。

doScan方法會掃描出符合指定規則的類,並且設定bean的一些屬性:

findCandidateComponents方法就是找出備選的bean,其內部呼叫scanCandidateComponents,scanCandidateComponents實現如下:

其工作過程:掃描指定包路徑下面的所有.class檔案,然後迴圈判斷是否符合過濾規則。isCandidateComponent的實現如下:

	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}

如果這一步判斷通過了,那麼就會進行第二步判斷:

	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
	}

這一步會過濾掉不是介面的類。找到符合條件的介面,就會new一個ScannedGenericBeanDefinition,放入到Set<BeanDefinition>進行返回。返回到doScan方法,裡面會對bean進行一些屬性設定:beanName、scope(預設是singleton)等。

然後會註冊到DefaultListableBeanFactory(就是將beanName與bean放到map中,防止重複載入)。掃描載入了mapper bean以後,processBeanDefinitions方法就會對這些bean進行處理:

這裡設定了bean的beanClass,為後面的設定動態代理打下基礎,beanClass的值為MapperFactoryBean(MapperFactoryBean實現了FactoryBean介面)。

至此,@MapperScan的流程就走完了,mapper介面被初始化為bean(非完整bean,還