前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringFramework之ClassPathBeanDefinitionScanner

SpringFramework之ClassPathBeanDefinitionScanner

作者头像
克虏伯
发布2019-06-28 10:35:16
8590
发布2019-06-28 10:35:16
举报

1. ClassPathBeanDefinitionScanner

    先上一张图,如下图1.

                                                                                         图1

    ClassPathBeanDefinitionScanner在哪使用到的呢,看如下List-1,AnnotationConfigApplicationContext的构造方法中,初始化了ClassPathBeanDefinitionScanner,为什么传this进入呢,是因为ClassPathBeanDefinitionScanner的构造方法参数是BeanDefinitionRegistry类型的,ClassPathBeanDefinitionScanner要把扫描到是BeanDefinition注册到BeanDefinitionRegistry中。

List-1

代码语言:javascript
复制
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-2

List-2

代码语言:javascript
复制
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中。

List-3

代码语言:javascript
复制
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-4

代码语言:javascript
复制
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

代码语言:javascript
复制
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. 源码

(adsbygoogle = window.adsbygoogle || []).push({});

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. ClassPathBeanDefinitionScanner
  • 2. 拓展——mybatis扫描类
  • Reference
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档