扫描bean
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);
//对beanDefinition进行设置,注册
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;
}
findCandidateComponents(basePackage):扫描类路径以查找候选组件。具体扫描为父类的如下方法:
ClassPathScanningCandidateComponentProvider#scanCandidateComponents(String basePackage)
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
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);
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);
}
}
}
- 获取basePackage下的所有资源。通过Ant样式的PathMatcher查找与给定位置模式匹配的所有资源。 支持jar文件和zip文件以及文件系统中的资源。
- 遍历所有的resources,将resource包装成SimpleMetadataReader,SimpleMetadataReader包含读取resource注解元数据的AnnotationMetadataReadingVisitor:ASM类访问者,用于查找类名称和实现的类型以及在类上定义的注解,并通过AnnotationMetadata接口将其公开。
- 判断metadataReader是否满足条件: isCandidateComponent(MetadataReader metadataReader) 确定给定的类是否不匹配任何排除过滤器并且是否匹配至少一个包含过滤器。
- 若第3步满足则生成一个ScannedGenericBeanDefinition类型的BeanDefinition,接着判断该BeanDefinition是否满足条件:isCandidateComponent(AnnotatedBeanDefinition beanDefinition) 确定给定的bean定义是否符合候选条件。默认实现检查该类是否不是接口,并且不依赖于封闭的类。该方法可以在子类中覆盖。若满足则添加到候选者集合中
注意:上述两个isCandidateComponent方法都是可以子类重写的,mybatis就重写了第二个isCandidateComponent(AnnotatedBeanDefinition beanDefinition)
//mybatis的ClassPathMapperScanner重写的
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
包含过滤器includeFilters和排除过滤器excludeFilters
includeFilters和excludeFilters是在构造ClassPathBeanDefinitionScanner时声明的
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;
}
spirngboot扫描时的includeFilters
包含@Component和@ManagedBean
mybatis扫描时的includeFilters和excludeFilters
mybatis使用ClassPathMapperScanner扫描,ClassPathMapperScanner 继承了ClassPathBeanDefinitionScanner
构造ClassPathMapperScanne添加的 includeFilters和excludeFilters
if (acceptAllInterfaces) {
// default include filter that accepts all classes
addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
}
// exclude package-info.java
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
String className = metadataReader.getClassMetadata().getClassName();
return className.endsWith("package-info");
});