SpringFramework之ClassPathBeanDefinitionScanner

    Spring的版本是5.0.9.release.java

1. ClassPathBeanDefinitionScanner

    先上一張圖,以下圖1.git

                       

                                                                                         圖1github

    ClassPathBeanDefinitionScanner在哪使用到的呢,看以下List-1,AnnotationConfigApplicationContext的構造方法中,初始化了ClassPathBeanDefinitionScanner,爲何傳this進入呢,是由於ClassPathBeanDefinitionScanner的構造方法參數是BeanDefinitionRegistry類型的,ClassPathBeanDefinitionScanner要把掃描到是BeanDefinition註冊到BeanDefinitionRegistry中。spring

    List-1mybatis

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

	private final AnnotatedBeanDefinitionReader reader;

	private final ClassPathBeanDefinitionScanner scanner;


	/**
	 * Create a new AnnotationConfigApplicationContext that needs to be populated
	 * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
	 */
	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
...

    ClassPathBeanDefinitionScanner的最重要的方法是scan(),scan()又調用doScan(),以下List-2app

    List-2ide

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);//1
        for (BeanDefinition candidate : candidates) {
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {//2
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            if (checkCandidate(beanName, candidate)) {//3
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

    findCandidateComponents(),作了不少事情,咱們來分析下,findCandidateComponents()調用scanCandidateComponents(),scanCandidateComponents()中會在basePackage前面加上"classpaths:",再加上後綴"**/*.class",以後用PathMatchingResourcePatternResolver獲取該package下的全部類,並將每一個類封裝爲Resource,以下List-3,以後將每一個resource轉換爲MetadataReader,仔細看下1處isCandidateComponent(metadataReader)的處理,以下List-4,在List-4中,includeFilters默認添加了new AnnotationTypeFilter(Component.class),這意味着isCandidateComponent()方法中調用includeTypeFilter的TypeFilter.match時,只要這個類上標有@Component或者含有@Component的組合註解,那麼這個類就會被視爲Spring要處理的類來處理,不然不會考慮,includeTypeFilter的TypeFilter.match返回true時,以後調用isConditionMatch(),isConditionMatch方法中又調用conditionEvaluator的shuldSkip方法來判斷是否將這個類註冊到BeanFactory,conditionEvaluator.shuldSkip()方法處理的是@ConditionalOnXX的狀況,好比CondtionalOnBean等,有上面的分析能夠看出,先判斷類上是否有@Component或者其組合註解,若是有再處理加了@ConditionalOn的狀況,若是不符合則這個類不會解析爲BeanDefinition,進而不會註冊到BeanFactory中。post

    List-3this

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            if (resource.isReadable()) {
                try {
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    if (isCandidateComponent(metadataReader)) {//1
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        if (isCandidateComponent(sbd)) {
                            if (debugEnabled) {
                                logger.debug("Identified candidate component class: " + resource);
                            }
                            candidates.add(sbd);
                        }
                       ...
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    return candidates;
}

    List-4lua

private final List<TypeFilter> includeFilters = new LinkedList<>();
private final List<TypeFilter> excludeFilters = new LinkedList<>();
...
protected void registerDefaultFilters() {
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
        logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
    }
    try {
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
        logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
    }
    catch (ClassNotFoundException ex) {
        // JSR-330 API not available - simply skip.
    }
}
...
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;
}
...
private boolean isConditionMatch(MetadataReader metadataReader) {
    if (this.conditionEvaluator == null) {
        this.conditionEvaluator =
                new ConditionEvaluator(getRegistry(), this.environment, this.resourcePatternResolver);
    }
    return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
}

    List-3中,將符合要求的類解析爲BeanDefinition,並返回BeanDefiniton集合,回到了List-2中,會遍歷獲得的BeanDefinition集合,以後先判斷是不是AbstractBeanDefinition類型,若是是則調用postProcessBeanDefinition來處理,以後判斷是不是AnnotatedBeanDefinition類型,若是是則調用AnnotationConfigUtils.processCommonDefinitionAnnotations處理BeanDefinition,上述分析可知獲得的BeanDefinition是ScannedGenericBeanDefinition,因此這個倆個處理方法都會被調用,咱們來看下AnnotationConfigUtils.processCommonDefinitionAnnotations,以下List-5:

    List-5

public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
    processCommonDefinitionAnnotations(abd, abd.getMetadata());
}

static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
    AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);//1
    if (lazy != null) {
        abd.setLazyInit(lazy.getBoolean("value"));
    }
    else if (abd.getMetadata() != metadata) {
        lazy = attributesFor(abd.getMetadata(), Lazy.class);
        if (lazy != null) {
            abd.setLazyInit(lazy.getBoolean("value"));
        }
    }

    if (metadata.isAnnotated(Primary.class.getName())) {//2
        abd.setPrimary(true);
    }
    AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
    if (dependsOn != null) {//3
        abd.setDependsOn(dependsOn.getStringArray("value"));
    }

    if (abd instanceof AbstractBeanDefinition) {
        AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd;
        AnnotationAttributes role = attributesFor(metadata, Role.class);//4
        if (role != null) {
            absBd.setRole(role.getNumber("value").intValue());
        }
        AnnotationAttributes description = attributesFor(metadata, Description.class);//5
        if (description != null) {
            absBd.setDescription(description.getString("value"));
        }
    }
}
  1. 判斷類上是否有Lazy註解,若是是則取出value的值,設置到BeanDefinition中。
  2. 判斷類上是否有Primary註解,若是是則給BeanDefinition的Primary設置爲true。
  3. 判斷類上是否有DependsOn註解,若是是則獲取值並設置到BeanDefinition中。

    List-5中處理完了後,回到List-2,方法registerBeanDefinition將這個BeanDefinition註冊到Spring容器中。

2. 拓展——mybatis掃描類

    使用Spring-mybatis來集成mybatis時,MapperScannerConfigurer中使用ClassPathMapperScanner來掃描類,解析爲BeanDefinition後,將BeanDefinition的beanClass類型設置爲MapperFactoryBean——FactoryBean類型。

Reference

  1. 源碼
相關文章
相關標籤/搜索