Spring組件註冊註解之@ComponentScan,@ComponentScans

 

目錄

1. 說明

2. @ComponentScan註解屬性

3. @ComponentScan過濾規則說明

4. 自定義掃描過濾規則

5. @ComponentScan原理分析

6. @ComponentScans


1. 說明

與ComponentScan註解相對應的XML配置就是<context:component-scan/>, 根據指定的配置自動掃描package,將符合條件的組件加入到IOC容器中;

XML的配置方式如下:

	<context:component-scan
		base-package="com.yibai.spring.annotation" use-default-filters="false">
		<context:include-filter type="custom"
			expression="com.yibai.spring.annotation.filter.ColorBeanLoadFilter" />
		<context:exclude-filter type="annotation"
			expression="org.springframework.stereotype.Component" />
	</context:component-scan>

2. @ComponentScan註解屬性

@ComponentScan有如下屬性:

value:指定要掃描的package;
includeFilters=Filter[]:指定只包含的組件
excludeFilters=Filter[]:指定需要排除的組件;
useDefaultFilters=true/false:指定是否需要使用Spring默認的掃描規則:被@Component, @Repository, @Service, @Controller或者已經聲明過@Component自定義註解標記的組件;

在過濾規則Filter中:
FilterType:指定過濾規則,支持的過濾規則有
    ANNOTATION:按照註解規則,過濾被指定註解標記的類;
    ASSIGNABLE_TYPE:按照給定的類型;
    ASPECTJ:按照ASPECTJ表達式;
    REGEX:按照正則表達式
    CUSTOM:自定義規則;
value:指定在該規則下過濾的表達式;

3. @ComponentScan過濾規則說明

規則表達式說明

1. 掃描指定類文件
   @ComponentScan(basePackageClasses = Person.class)
2. 掃描指定包,使用默認掃描規則,即被@Component, @Repository, @Service, @Controller或者已經聲明過@Component自定義註解標記的組件;
   @ComponentScan(value = "com.yibai")
3. 掃描指定包,加載被@Component註解標記的組件和默認規則的掃描(因爲useDefaultFilters默認爲true)
   @ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.ANNOTATION, value = Component.class) })
4. 掃描指定包,只加載Person類型的組件
   @ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = Person.class) }, useDefaultFilters = false)
5. 掃描指定包,過濾掉被@Component標記的組件
   @ComponentScan(value = "com.yibai", excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = Component.class) })
6. 掃描指定包,自定義過濾規則
   @ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.CUSTOM, value = ColorBeanLoadFilter.class) }, useDefaultFilters = true)

4. 自定義掃描過濾規則

自定義規則實現類ColorBeanLoadFilter 

/**
 * Project Name:yibai-spring-annotation
 * File Name:ColorBeanLoadFIlter.java
 * Package Name:com.yibai.spring.annotation.filter
 * Date:2019年1月5日下午12:15:54
 * Copyright (c) 2019, www.windo-soft.com All Rights Reserved.
 *
*/

package com.yibai.spring.annotation.filter;

import java.io.IOException;

import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import com.yibai.spring.annotation.bean.color.Color;

public class ColorBeanLoadFilter implements TypeFilter {

	@Override
	public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
			throws IOException {
		// 當前被掃描類的註解信息
		@SuppressWarnings("unused")
		AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
		// 當前被掃描類信息
		ClassMetadata classMetadata = metadataReader.getClassMetadata();
		// 當前被掃描類資源信息
		@SuppressWarnings("unused")
		Resource resource = metadataReader.getResource();
		try {
			String className = classMetadata.getClassName();
			Class<?> forName = Class.forName(className);
			if (Color.class.isAssignableFrom(forName)) {
				// 如果是Color的子類,就加載到IOC容器
				return true;
			}
		} catch (ClassNotFoundException e) {
//			e.printStackTrace();
		}
		return false;
	}

}

5. @ComponentScan原理分析

@ComponentScan的解析器是org.springframework.context.annotation.ComponentScanAnnotationParser;

useDefaultFilters = true指定的規則過濾器是 org.springframework.core.type.filter.AnnotationTypeFilter;’

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {

        /** componentScan爲@ComponentScan的註解信息:
            {value=[com.yibai.spring.annotation], basePackages=[com.yibai.spring.annotation], lazyInit=false, scopeResolver=class org.springframework.context.annotation.AnnotationScopeMetadataResolver, scopedProxy=DEFAULT, nameGenerator=interface org.springframework.beans.factory.support.BeanNameGenerator, useDefaultFilters=true, basePackageClasses=[], includeFilters=[], resourcePattern=**/*.class, excludeFilters=[]}
*/

		Assert.state(this.environment != null, "Environment must not be null");
		Assert.state(this.resourceLoader != null, "ResourceLoader must not be null");

		ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
				componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

		//....
        //..
        //.
        //中間代碼省略了,大概做的事情就是創建包掃描器,然後被@CompontentScan註解設置的屬性都設置搭配掃描器中,然後準備掃描
        
        // 經過一些列的scanner的屬性設置,進入掃描流程
		return scanner.doScan(StringUtils.toStringArray(basePackages));
	}

然後進入org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(String...) 進行掃描,並生成組件定義信息;

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

        //下面就是將掃描出的組件實加載到IOC容器中;
		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);
				registerBeanDefinition(definitionHolder, this.registry);
			}
		}
	}
	return beanDefinitions;
}

通過org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.findCandidateComponents(String)找出符合條件的組件

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
       Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
	try {
        //classpath*:com/yibai/spring/annotation/**/*.class
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
				resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        //resources 就是指定包下的所有類
		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;
}

通過org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider.isCandidateComponent(MetadataReader)方法,根據過濾器規則判斷類是否滿足條件

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)) {
            // 判斷是否條件滿足
			return isConditionMatch(metadataReader);
		}
	}
	return false;
}

當斷點到自定義過濾類裏面的時候,調用棧如下

 

 

6. @ComponentScans

可以一次聲明多個@ComponentScan

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)  //指定ComponentScan可以被ComponentScans作爲數組使用
public @interface ComponentScan {
}


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScans {
    
	ComponentScan[] value();

}
@ComponentScans(value = { @ComponentScan(value = "com.yibai.spring.annotation"),
		@ComponentScan(value = "com.yibai.spring.annotation", includeFilters = {
				@Filter(type = FilterType.CUSTOM, value = ColorBeanLoadFilter.class) }) })
public class MainConfig {

	@Bean(name = "pers", initMethod = "init", destroyMethod = "destory")
	public Person person() {
		return new Person();
	}

}

 

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