标签详解

配置以及代码如下

配置


    
        
    

代码

package com.test.controller;

import org.springframework.stereotype.Controller;

@Controller
public class UserController {

}

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

源码分析

(1)前面讲过[<context:annotation-config/>自动扫描标签详解],当解析自定义的标签时,就由BeanDefinitionParser接口的子类来完成的。而NamespaceHandler 的同样是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来解析标签。
(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 beanDefinitions = scanner.doScan(basePackages);
         //注册其他的BeanPostProcessor,这个放在最后讲
        registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

        return null;
    }

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

Set beanDefinitions = scanner.doScan(basePackages);

进入doScan方法中

    protected Set doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set beanDefinitions = new LinkedHashSet();
        for (String basePackage : basePackages) {
//这里是重点,这里将对指定的包下的类进行扫描,拿到候选的扫描类
            Set 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 findCandidateComponents(String basePackage) {
        Set candidates = new LinkedHashSet();
        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;
    }

未待完续。。。

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

你可能感兴趣的:(标签详解)