美文网首页Spring工作生活
Spring Component-Scan流程

Spring Component-Scan流程

作者: 晴天哥_王志 | 来源:发表于2019-07-05 10:48 被阅读0次

    开篇

     在Spring 加载BeanDefinition的这篇文章中我们着重分析了BeanDefinition的加载过程并提到了对于<component-scan>标签的解析,这篇文章我们深入细化下Spring针对如@Component注解的加载过程。

     整个流程核心在于1、解析basePackage的路径;2、扫描basePackage路径查找class文件;3、判断是否符合过滤条件,经过上述3个步骤后返回符合要求的BeanDefinition对象。

    Component-scan流程分析

    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());
        }
    }
    

    说明:

    • 1、注册component-scan对应的解析器ComponentScanBeanDefinitionParser。
    public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
    
        private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
        private static final String RESOURCE_PATTERN_ATTRIBUTE = "resource-pattern";
        private static final String USE_DEFAULT_FILTERS_ATTRIBUTE = "use-default-filters";
        private static final String ANNOTATION_CONFIG_ATTRIBUTE = "annotation-config";
        private static final String NAME_GENERATOR_ATTRIBUTE = "name-generator";
        private static final String SCOPE_RESOLVER_ATTRIBUTE = "scope-resolver";
        private static final String SCOPED_PROXY_ATTRIBUTE = "scoped-proxy";
        private static final String EXCLUDE_FILTER_ELEMENT = "exclude-filter";
        private static final String INCLUDE_FILTER_ELEMENT = "include-filter";
        private static final String FILTER_TYPE_ATTRIBUTE = "type";
        private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression";
    
    
        @Override
        @Nullable
        public BeanDefinition parse(Element element, ParserContext parserContext) {
            // 获取base-package路径
            String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
            basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
            String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
                    ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    
            // 针对base-package的路径进行扫描
            ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
            Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    
            // 注册BeanDefinition对象
            registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    
            return null;
        }
    }
    

    说明:

    • 2、创建ClassPathBeanDefinitionScanner类型的scanner对象。
    • 3、通过 scanner.doScan()扫描符合base_package的class文件。
    public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
    
        protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    
            Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
            for (String basePackage : basePackages) {
                // 根据basePackage去查找
                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) {
                        // 组装BeanDefinition默认属性
                        postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                    }
    
                    // 解析类中的注解配置
                    if (candidate instanceof AnnotatedBeanDefinition) {
                        AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                    }
    
                    // 检查符合条件的BeanDefinition并注册
                    if (checkCandidate(beanName, candidate)) {
                        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                        definitionHolder =
                                AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                        beanDefinitions.add(definitionHolder);
                        registerBeanDefinition(definitionHolder, this.registry);
                    }
                }
            }
            return beanDefinitions;
        }
    }
    

    说明:

    • 4、findCandidateComponents方法负责查找候选集,内部继续跟进查看。
    public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
    
        public Set<BeanDefinition> findCandidateComponents(String basePackage) {
            if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
                return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
            }
            else {
                // 暂时关注这部分逻辑
                return scanCandidateComponents(basePackage);
            }
        }
    }
    
    
    public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
    
        private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
            Set<BeanDefinition> candidates = new LinkedHashSet<>();
            try {
                String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                        resolveBasePackage(basePackage) + '/' + this.resourcePattern;
    
                // 真正执行查找的逻辑,class文件查找的实现细节
                Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
    
                for (Resource resource : resources) {
                    if (resource.isReadable()) {
                        try {
                            MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
    
                            // 判断是否符合候选集,核心的判断在于isCandidateComponent
                            if (isCandidateComponent(metadataReader)) {
                                ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                                sbd.setResource(resource);
                                sbd.setSource(resource);
                                if (isCandidateComponent(sbd)) {
                                    candidates.add(sbd);
                                }
                            }
                        }
                        catch (Throwable ex) {
                        }
                    }
                }
            }
            catch (IOException ex) {
            }
    
            return candidates;
        }
    }
    

    说明:

    • 5、getResourcePatternResolver().getResources(packageSearchPath)负责查找对象,内部针对文件、jar包等多种数据结构进行查找,细节可以深究一下。
    • 6、通过isCandidateComponent()判断是否是候选集对象。
    public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
    
        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;
        }
    }
    
    
    public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
    
        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.trace("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.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
            }
            catch (ClassNotFoundException ex) {
                // JSR-330 API not available - simply skip.
            }
        }
    }
    
    • 7、isCandidateComponent先经过excludeFilters进行过滤,在经过includeFilters过滤,最后返回是否符合。
    • 8、includeFilters通过registerDefaultFilters注册过滤器,默认我们发现有Componet注解。

    参考文章

    Spring源码-IOC容器(九)-Component-Scan源码解析

    相关文章

      网友评论

        本文标题:Spring Component-Scan流程

        本文链接:https://www.haomeiwen.com/subject/gstxhctx.html