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