Spring源码之scan扫描

入口:在解析配置类时,处理@ComponentScans注解,就会调用doScan方法!

关键类及方法:ClassPathBeanDefinitionScanner-->scan-->doScan

下面分析doScan方法

1.doScan流程分析

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) {//扫描路径
			// 核心:扫描,生成BeanDefinition
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

			// 处理每个candidate
			for (BeanDefinition candidate : candidates) {
				// 解析scope注解
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				// 生成BeanName
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					// 解析@Lazy、@Primary、@DependsOn、@Role、@Description,设置到beanDefinition中
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}

				// 检查Spring容器中是否已经存在该beanName
				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;
	}

1.findCandidateComponents,后面详细讲这个方法

传入包路径,扫描,并进行条件匹配,得到BeanDefinition,然后将BeanDefinition放到candidates集合中,将其作为candidates(候选),后面继续筛选。

它中包含两个分支,一是addCandidateComponentsFromIndex,表示使用Spring.components文件加快扫描速度;二是多数进入的分支,方法是scanCandidateComponents。两者的内部实现基本相同。

2.拿到所有BeanDefinition(for循环),然后去解析

3.resolveScopeMetadata

解析scope注解,将其设置到BeanDefinition的属性中

4.generateBeanName

首先解析@Component注解有没有指定bean的名字,如果没有指定,就返回null;

然后生成默认的名字,将命名首字母小写(属于JDK部分)作为beanName。当第一个字母和第二个字母都是大写时,则直接使用该类名作为beanName。

5.postProcessBeanDefinition

继续设置BeanDefinition的默认值,比如,初始化方法、懒加载属性等。

6.processCommonDefinitionAnnotations

继续解析注解,设置到BeanDefinition对应的属性上,包括,@Lazy、@Primary、@DependsOn、@Role、@Description

7.checkCandidate(beanName, candidate)

检查Spring容器中是否已经存在该beanName,验证是否兼容。

如果兼容,返回false表示不会重新注册到Spring容器中,如果不兼容则会抛异常。(说明存在相同BeanName的Bean)。

判断兼容的方法是isCompatible,判断扫描的bean是否兼容,如果扫描的类文件完全相同(通过resource来判断),则bean是兼容的。

8.registerBeanDefinition

注册<beanName,BeanDefinition>到BeanDefinitionMap中去

2.findCandidateComponents方法分析

核心:找到所有候选bean!

流程说明:核心方法是scanCandidateComponents,通过扫描得到BeanClassName,这些都可以生成BeanDefinition。

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			return scanCandidateComponents(basePackage);//
		}
	}

它中包含两个分支,一是addCandidateComponentsFromIndex,表示使用Spring.components文件加快扫描速度;二是多数进入的分支,方法是scanCandidateComponents。两者的内部实现基本相同。

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			// 获取basePackage下所有的文件资源
			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);
						// excludeFilters、includeFilters判断
						if (isCandidateComponent(metadataReader)) { // @Component-->includeFilters判断 过滤器进行判断
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);// 构造beanDefinition
							sbd.setSource(resource);

							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);// 将可以成为bean的beanDefition加入候选
							}
							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;
	}

1.getResources

找到所有的.class文件(找到所有的类)。

2.metadataReader

元数据读取器:读取注解的信息、类的信息(类的名字、父子类信息)等,底层用的ASM技术。(此时并没有将这个类加载到JVM)

3.isCandidateComponent(metadataReader)

判断当前类是否可以跳过(shouldSkip)。

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}

		// 符合includeFilters的会进行条件匹配,通过了才是Bean,也就是先看有没有 @Component,再看是否符合@Conditional
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}

在Spring容器启动的时候,已经设定了excludeFilters和includeFilters(排除过滤器和包含过滤器),此时进行过滤。(一般这个时候只会判断@Component注解,spring启动时只将这一个注解设置了,如果用户没有特殊指定其他的Filter属性)。

通过过滤器过滤后,还需要进行条件匹配(条件注解)。(类加了@Conditional 或者 该类实现了Condition接口),如果类实现了Condition接口,并重写了match方法,那么就会调用match方法进行过滤。

4.ScannedGenericBeanDefinition

构造BeanDefinition。

setBeanClassName(this.metadata.getClassName());这里只是把className设置到BeanDefinition中(此时并不是真正的类,后面创建bean的时候,才会把创建的对象赋值给这个属性)。

5.isCandidateComponent(sbd)

判断该类是否能成为Bean。

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
	}

1)isIndependent:(是否独立)内部类不能成为一个Bean

2)isConcrete:(isInterface() || isAbstract())接口和抽象类不能成为Bean

3)是抽象类且类中存在使用@Lookup注解的方法, 则该类可以成为Bean,会将Lookup("XXX")中的XXX扫描成为BeanDefinition,并获取该XXX的bean。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值