<context:component-scan>标签详解

配置以及代码如下

配置

<!-- 配置自动扫描的包 -->
    <context:component-scan base-package="com.test.controller">
        
    </context:component-scan>

代码

package com.test.controller;

import org.springframework.stereotype.Controller;

@Controller
public class UserController {

}

当我们在配置文件中配置了<context:component-scan>,那么spring将自动帮我们扫描指定包下的类是否标注了@Component,@Repository,@Service,@Controller这几个注解,如果标注了那么spring将自动帮我们实例化。那么spring是如何做到的呢?下面来看源码分析。

源码分析

(1)前面讲过[<context:annotation-config/>自动扫描标签详解],当解析自定义的标签时,就由BeanDefinitionParser接口的子类来完成的。而NamespaceHandler <context:component-scan>的同样是ContextNamespaceHandler 类

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
        registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
        registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
    }

}

最终由ComponentScanBeanDefinitionParser来解析<context:component-scan>标签。
(2)看下ComponentScanBeanDefinitionParse的解析方法parse()

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
//获取要扫描的包名,也就是base-package元素对应的值,我这里配置的是com.test.controller
        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);
         //注册其他的BeanPostProcessor,这个放在最后讲
        registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

        return null;
    }

从上面的代码可以看出,先是获取到<context:component-scan>标签中配置的base-package元素,通过这个元素的值知道要扫描那些类,然后执行扫描,获取标注了@Component,@Repository,@Service,@Controller这几个注解的类,并注册到beanDefinitionMap中,在容器触发getBean()方法,将对这些类进行实例化,并保存在spring的注册表(当然这是在Singleton情况下)
(3)下面来看具体的扫描过程

Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);

进入doScan方法中

    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
        for (String basePackage : basePackages) {
//这里是重点,这里将对指定的包下的类进行扫描,拿到候选的扫描类
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            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) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                }
                if (checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
//这里注册到beanDefinitionMap中
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;
    }

重点是继承自父类ClassPathScanningCandidateComponentProvider 的findCandidateComponents方法,这里将进行扫描
来看下这个findCandidateComponents方法

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
        try {
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    resolveBasePackage(basePackage) + "/" + this.resourcePattern;
            Resource[] resources = this.resourcePatternResolver.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 = this.metadataReaderFactory.getMetadataReader(resource);
                        if (isCandidateComponent(metadataReader)) {
                            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);
                            }
                            else {
                                if (debugEnabled) {
                                    logger.debug("Ignored because not a concrete top-level class: " + resource);
                                }
                            }
                        }
                        else {
                            if (traceEnabled) {
                                logger.trace("Ignored because not matching any filter: " + resource);
                            }
                        }
                    }
                    catch (Throwable ex) {
                        throw new BeanDefinitionStoreException(
                                "Failed to read candidate component class: " + resource, ex);
                    }
                }
                else {
                    if (traceEnabled) {
                        logger.trace("Ignored because not readable: " + resource);
                    }
                }
            }
        }
        catch (IOException ex) {
            throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
        }
        return candidates;
    }

未待完续。。。

[朝阳区尼克杨]
转载请注明原创出处,谢谢啦!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,952评论 19 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 47,317评论 6 342
  • 来源:关于Spring IOC (DI-依赖注入)你需要知道的一切作者:zejian Dao层(AccountDa...
    杨井阅读 5,464评论 0 27
  • 小姜老师要求学生,作文中凡是她批改过的地方都要重新抄一遍,以加深印象。 小明以《我的老师》为题写了一篇作文:我的...
    梓毓爸阅读 399评论 1 6
  • “亲爱的,我今晚要露宿街头了。”刚离开深圳4天的H给我发来微信。 我知道肯定出事了,这真在我的预料之中,马上打电话...
    向行阅读 384评论 11 6

友情链接更多精彩内容