前言
本篇主要讲解在指定的基础包中执行扫描注册BeanDefinition: doscan(String... basePackages)方法以及扫描过滤,匹配。
解析
主要方法:ClassPathBeanDefinitionScanner#doScan(String... basePackages)在指定的基础包中执行扫描,返回已注册的bean定义。
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 扫描候选组件的类路径
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
//生成bean名称
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);
//注册bean定义
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
1、ClassPathScanningCandidateComponentProvider#findCandidateComponents(basePackage), 扫描候选组件的类路径
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
//根据索引获得bean定义,配合spring-context-indexer使用,有兴趣得自己去了解一下,这里不做具体得解析
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
//扫描获得bean定义
return scanCandidateComponents(basePackage);
}
}
2、ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePackage)
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//classpath*:com/zxj/***/service/**/*.class ant path模式串
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//用ant path获取到文件内容,文件和path(class文件)
Resource[] resources = getResourcePatternResolver().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 = getMetadataReaderFactory().getMetadataReader(resource);
//过滤匹配排除excludeFilters排除过滤器(可以没有),包含includeFilter中的包含过滤器(至少包含一个)。
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
//判断是否是合格的bean定义
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;
}
3、ClassPathScanningCandidateComponentProvider#isCandidateComponent(MetadataReader metadataReader)
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;
}
excludeFilters和includeFilters有那几种定义呢?
过滤器类型 | 示例表达 | 描述 |
---|---|---|
annotation (default) |
| 要在目标组件中的类型级别出现的注释。 |
assignable |
| 目标组件可分配给(扩展或实现)的类(或接口)。 |
aspectj |
| 要由目标组件匹配的AspectJ类型表达式。 |
regex |
| 要由目标组件类名匹配的正则表达式。 |
custom |
| org.springframework.core.type .TypeFilter 接口的自定义实现。 |
例如:
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
...
}
默认注册的ClassPathScanningCandidateComponentProvider#registerDefaultFilters包含过滤器 AnnotationTypeFilter(Component.class)。作用:@Component及其子类都会匹配到。
@Component子类: @Repository @Controller @service
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.
}
}
这里就体现了一个扩展点,例如我自己定义一个@myservice注解,在匹配的时候就会匹配到,就能够被扫描进bean定义容器。
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface MyService {
String value() default "";
}
4、具体的匹配方法:
1、AbstractTypeHierarchyTraversingFilter#match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
//
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// This method optimizes avoiding unnecessary creation of ClassReaders
// as well as visiting over those readers.
//匹配自己
if (matchSelf(metadataReader)) {
return true;
}
ClassMetadata metadata = metadataReader.getClassMetadata();
if (matchClassName(metadata.getClassName())) {
return true;
}
if (this.considerInherited) {
String superClassName = metadata.getSuperClassName();
if (superClassName != null) {
// Optimization to avoid creating ClassReader for super class.
Boolean superClassMatch = matchSuperClass(superClassName);
if (superClassMatch != null) {
if (superClassMatch.booleanValue()) {
return true;
}
}
else {
// Need to read super class to determine a match...
try {
if (match(metadata.getSuperClassName(), metadataReaderFactory)) {
return true;
}
}
catch (IOException ex) {
logger.debug("Could not read super class [" + metadata.getSuperClassName() +
"] of type-filtered class [" + metadata.getClassName() + "]");
}
}
}
}}
2、AnnotationTypeFilter#matchSelf(MetadataReader metadataReader)
@Override
//父类定义的钩子方法,子类实现。 匹配注解
protected boolean matchSelf(MetadataReader metadataReader) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
//metadata.hasAnnotation(this.annotationType.getName()) 例如如果当前类上的注解为@Service,那么元注解的annotationSet属性则为Service 自己
return metadata.hasAnnotation(this.annotationType.getName()) ||
//metadata.hasMetaAnnotation(this.annotationType.getName()) 例如如果当前类上的注解为@Service,那么元注解的metaAnotationMap则有@Service和@Component,自己和父类
(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}
里面的扩展还有:org.springframework.core.type .TypeFilter接口的自定义实现,去匹配注解,或者其他,匹配完成后注册bean定义。例如:
public class MyTypeFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
// 使用metadataReader中的类信息、注解信息来进行你的过滤判断逻辑
return metadataReader.getClassMetadata().getClassName().equals(Abean.class.getName());
}
}