spring-ApplicationCotext初始化之component-scan

    在我们配置的Spring-ApplicationContext文件中,context:component-scan是一个很重要的节点,它主要能帮助我们扫描指定包下面,需要加载到beanFactory容器的类,现在我们就来钻研一下这个扫描和加载的过程。

    前面需要注意的地方在http://singleant.iteye.com/blog/1177358中已经说得很清楚了,现在我们只讨论当文件解析器遇到context:component-scan时的操作过程。

    component-scan主要分为以下几步:

    1.根据context获取到相应的namespaceUri,即http://www.springframework.org/schema/context

    2.根据namespaceUri获取到相应的NamespaceHandler或者是className,如果是className,则还需要利用反射进行实例化,得到的是ContextNamespaceHandler,

    3.再根据component-scan获取相应的parser,ComponentScanBeanDefinitionParser

	public BeanDefinition parse(Element element, ParserContext parserContext) {
		return findParserForElement(element, parserContext).parse(element, parserContext);
	}
        private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
		String localName = parserContext.getDelegate().getLocalName(element);
		BeanDefinitionParser parser = this.parsers.get(localName);
		if (parser == null) {
			parserContext.getReaderContext().fatal(
					"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
		}
		return parser;
	}

    4.根据base-package带进来的参数,分割成数组

String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
    其中
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS = ",; \t\n";

     因此在base-package中的不同包可以用逗号,分号,制表符和换行符分割开,甚至可以混合使用。

5.然后就是新建scanner

protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
		XmlReaderContext readerContext = parserContext.getReaderContext();

		boolean useDefaultFilters = true;
		if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
			useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
		}

		// Delegate bean definition registration to scanner class.
		ClassPathBeanDefinitionScanner scanner = createScanner(readerContext, useDefaultFilters);
		scanner.setResourceLoader(readerContext.getResourceLoader());
		scanner.setEnvironment(parserContext.getDelegate().getEnvironment());
		scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
		scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
		if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
			scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
		}
		try {
			parseBeanNameGenerator(element, scanner);
		}
		catch (Exception ex) {
			readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
		}

		try {
			parseScope(element, scanner);
		}
		catch (Exception ex) {
			readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
		}

		parseTypeFilters(element, scanner, readerContext, parserContext);

		return scanner;
	}

    component-scan一共有三个attribute可以配置annotation-config,use-default-filters还有base-package,其中use-default-filters的默认值是true,这样会有三个includeFilter和0个excludeFilter添加,这个后面再详说。

6.然后就可以真正的scanner.doScan(basePackages),进入搜索环节

                ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
		Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
		registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
                for (String basePackage : basePackages) {
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
                }
        //这里因代码太多,删掉了一些log代码和异常捕捉代码
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
                //此时传入的 basePackage="com.lh708.controller"
		Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
		try {
                        //将包名转化为文件夹名 classpath*:com/lh708/controller/**/*.class
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + "/" + this.resourcePattern;
                        //这一步会现将classpath*:com/lh708/controller/**/*.class转换为本地文件夹路径,然后搜索文件夹下所有已.class结尾的文件,并封装到Resource返回
			Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (Resource resource : resources) {
				if (resource.isReadable()) {
					try {
						MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                                                if (isCandidateComponent(metadataReader)) {    //NOTE 1 
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setResource(resource);
							sbd.setSource(resource);
							if (isCandidateComponent(sbd)) {        //NOTE 2
								candidates.add(sbd);
							}
						}
					}
				}
			}
		}
		return candidates;
	}

NOTE 1: 这里会进行Filter检验,上文提到的use-default-filters就与此相关,没有配置的话,默认是true,只会装在三个includeFilters,即三个AnnotationTypeFilter:Component,ManagedBean以及Named

	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, this.metadataReaderFactory)) {
				return false;
			}
		}
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, this.metadataReaderFactory)) {       //NOTE 3
				return isConditionMatch(metadataReader);    //当次类有被Conditional修饰时,并且满足忽略条件时,也会返回FALSE
			}
		}
		return false;
	}

NOTE 2:这里会进行抽象,接口和内部类检查

	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
	}

	@Override
	public boolean isConcrete() {
            //不可为接口或抽象类
		return !(this.isInterface || this.isAbstract);
	}
	@Override
	public boolean isIndependent() {
            //不可为内部类
		return (this.enclosingClassName == null || this.independentInnerClass);
	}

NOTE 3:以Component为例,当这个类有被Component或者其衍生注解如Controller,Service,Repository修饰,返回true

	protected boolean matchSelf(MetadataReader metadataReader) {
		AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
		return metadata.hasAnnotation(this.annotationType.getName()) ||
				(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
	}

综上所述,能被检测并且正常添加到candidates应该满足几个条件

1.在被搜索的包下面

2.不是接口或抽象类,也不是内部类

3.不符合excludeFilters的条件,满足includeFilters的条件

4.被Conditional修饰时,需要看条件决定

发布了22 篇原创文章 · 获赞 1 · 访问量 8789
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章