以下源码基于4.x版本进行分析
Spring的扫描基于ClassPathBeanDefinitionScanner。扫描对应的逻辑有该类的scan方法进行实现。接下来我们来看该类对应的源代码。主要的扫描逻辑都是在doScan方法中。
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
doScan(basePackages);
// 注册系统中的配置类处理器
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
接下来在键入doScan方法中进行源代码的查看。大致脉络为,循环包路径的集合,找到满足条件的BeanDefinition。在对这些满足条件的BeanDefinition进行属性的赋值后对其进行注册。
protected SetdoScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); //创建bean定义的holder对象用于保存扫描后生成的bean定义对象 Set beanDefinitions = new LinkedHashSet<>(); //循环我们的包路径集合 for (String basePackage : basePackages) { //找到候选的Compents Set candidates = findCandidateComponents(basePackage); for (BeanDefinition candidate : candidates) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); //设置我们的beanName String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); //处理@AutoWired相关的,设置默认的信息 if (candidate instanceof AbstractBeanDefinition) { //对beanDefination设置默认值 postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } //处理jsr250相关的组件 if (candidate instanceof AnnotatedBeanDefinition) { //设置Lazy等注解 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } //把我们解析出来的组件bean定义注册到我们的IOC容器中,检查是否已有对应的beanDefination 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; }
那么什么是满足条件的beanDefinition呢?接下来我们进行findCandidateComponents方法的深入研究。主要逻辑还是在scanCandidateComponents中。if分支语句主要是为了减少扫描效率使用的,如果在META-INF/spring.components有该文件。那么则会按照spring.components定义的去进行扫描而走if逻辑。在文件中我们可以这样进行定义
com.xyz.User = org.springframework.stereotype.Component
public SetfindCandidateComponents(String basePackage) { //是否有META-INF/spring.components文件,有的话去读取其中的文件进行bean注册 if (this.componentsIndex != null && indexSupportsIncludeFilters()) { return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { //得到带有beanClass属性的beanDefination return scanCandidateComponents(basePackage); } }
接下来看最核心的扫描逻辑scanCandidateComponents
private SetscanCandidateComponents(String basePackage) { Set candidates = new LinkedHashSet<>(); try { //把我们的包路径转为资源路径 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; //把资源路径变成我们需要解析的路径下的所有class路径 asm技术 Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); //需要我们的resources集合 for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } //判断当的是不是课读的 if (resource.isReadable()) { try { //元数据读取器 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { //包装成为一个ScannedGenericBeanDefinition ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); //并且设置class资源 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; }
接下来我们看下该代码对应的流程图展示如下:



