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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章