我是靠谱客的博主 虚拟大神,最近开发中收集的这篇文章主要介绍Spring-component-scan源码探讨,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

在Spring配置文件中,可通过context:component-scan元素,指定包路径,Spring在创建容器时会扫描有注解@Component、@Repository、@Service、@Controller的类并注册bean到容器中,常见的用法:

<context:component-scan base-package="com.course.dao,com.course.service">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

Spring将会扫描路径com.course.dao和com.course.service下的有@Component、@Repository、@Service注解的类,并排除@Controller注解的类,

下面看看源码实现原理。 
Spring版本:5.0.6

原理
通过上一篇文章Spring-ClassPathXmlApplicationContext源码探讨-解析XML文件 知道,context:component-scan元素的解析类是 ComponentScanBeanDefinitionParser,其实现BeanDefinitionParser接口的parse()方法,代码:

 

public BeanDefinition parse(Element element, ParserContext parserContext) {
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

    // Actually scan for bean definitions and register them.
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    //注册组件,触发bean注册事件
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

    return null;
}

configureScanner(parserContext, element)方法创建并配置ClassPathBeanDefinitionScanner 扫描器类相关信息,如过滤器exclude-filter,在创建扫描器类ClassPathBeanDefinitionScanner时,会调用其父类ClassPathScanningCandidateComponentProvider#registerDefaultFilters()方法注册默认包含的注解,代码:
 

protected void registerDefaultFilters() {
    //@Repository、@Service、@Controller注解的元注解包含了@Component
    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.
    }
}

接着调用扫描器的方法scanner.doScan(basePackages)扫描路径,源码:

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) {
        //根据路径找到合适的bean
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            //设置bean的scope
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                //设置该bean是否可以通过type被其他bean注解
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            //处理bean的Lazy、Primary、DependsOn等注解
            if (candidate instanceof AnnotatedBeanDefinition) {
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            //判断目标bean是否合适
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                //如果使用代理模式,创建目标类的代理类
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                //注册bean
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

重要之处已经添加注释,下面再看看具体几个重要方法。

ClassPathScanningCandidateComponentProvider#findCandidateComponents(String basePackage)方法

代码:

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    }
    else {
        return scanCandidateComponents(basePackage);
    }
}
//删除日志输出代码
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);
        for (Resource resource : resources) {
            if (resource.isReadable()) {
                try {
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    //判断该bean是包含还是过滤
                    if (isCandidateComponent(metadataReader)) {
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        //判断该bean符合独立或具体、抽象、有Lookup注解条件
                        if (isCandidateComponent(sbd)) {                        
                            candidates.add(sbd);
                        }
                        else {
                        }
                    }
                    else {      
                    }
                }
                catch (Throwable ex) {
                    throw new BeanDefinitionStoreException(
                            "Failed to read candidate component class: " + resource, ex);
                }
            }
            else {                  
            }
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    return candidates;
}

AnnotationScopeMetadataResolver#resolveScopeMetadata(definition)方法

public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
    //如果解析不到Scope注解,将使用默认scope:singleton
    ScopeMetadata metadata = new ScopeMetadata();
    if (definition instanceof AnnotatedBeanDefinition) {
        AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
        AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
                annDef.getMetadata(), this.scopeAnnotationType);
        if (attributes != null) {
            metadata.setScopeName(attributes.getString("value"));
            ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
            if (proxyMode == ScopedProxyMode.DEFAULT) {
                proxyMode = this.defaultProxyMode;
            }
            metadata.setScopedProxyMode(proxyMode);
        }
    }
    return metadata;
}

 

最后

以上就是虚拟大神为你收集整理的Spring-component-scan源码探讨的全部内容,希望文章能够帮你解决Spring-component-scan源码探讨所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部