spring源码doScan()扫描过程

1.spring启动的时候会调用ClassPathBeanDefinitionScanner这个类里面的doScan()方法去扫描包下的路径

public int scan(String... basePackages) {
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

		doScan(basePackages);

		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) {
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}

2.这个方法主要就是进行扫描忘记路径,然后得到一个beanDefinition集合

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,主要进行行excludeFilters、includeFilters判断和Conditional判断
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			//遍历BeanDefinition
			for (BeanDefinition candidate : candidates) {
				//解析scope注解信息
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				//设置scope注解信息
				candidate.setScope(scopeMetadata.getScopeName());
				主要是解析Component有没有设置bean的名字,有则直接返回,
				// 没有则根据短名构造一个(如果写的名字第1、2个字符是大写则直接返回,否则直接将第一个字符转成小写返回)
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					//主要是给BeanDefinition设置一些默认的值
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					// 解析@Lazy、@Primary、@DependsOn、@Role、@Description注解,为这些注解设置值
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}

				// 检查Spring容器中是否已经存在该beanName,有的话会判断是否匹配,匹配则不会加入spring容器
				//不匹配则抛出异常,没有则直接加入spring容器
				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;
	}

3.这里面首先是构造一个集合beanDefinition,然后遍历传进来的扫描路径,通过findCandidateComponents这个方法处理扫描路径,我们来看看这个方法主要是干什么的:

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		//解析是否有文件写的有component注解
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			return scanCandidateComponents(basePackage);
		}
	}
if()里面就是判断META-INF目录下面是否有写components文件(在该文件里面可以直接定义bean)然后在解析这个文件,其逻辑大致和下面一样。
   通过scanCandidateComponents这个方法返回BeanDefinition集合。该方法主要是获得扫描路径下面的所有file对象,然后读取注解信息,判断与excludeFilters、includeFilters是否匹配以及Conditional判断,最后判断是否为接口、抽象类、独立类、以及lookup注解,最终返回BeanDefinition
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			// 获取basePackage下所有的文件资源
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			//classpath文件下的所有file对象
			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 {
						//resource:AppConfig.class
						//元数据读取器,读取注解的信息,类的信息,接口抽象类,父类等
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						/*读取类的信息进行判断excludeFilters、includeFilters判断,以及Conditional判断,
						是@Conditional注解则不是一个bean
						*/
						//判断是不是一个bean
						if (isCandidateComponent(metadataReader)) { //@Component-->includeFilters

							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							//把bean的属性设置进去,主要是名字
							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);
					}
				}
			}
		}
getMetadataReader是元数据读取器,主要是读取注解的信息,类的信息,接口抽象类,父类等isCandidateComponent主要是判断是否与excludeFilters、includeFilters匹配,spring启动的时候会为includeFilters设置一个默认值也就是@Component
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		for (TypeFilter tf : this.excludeFilters) {
			//和排除过滤器匹配,不是一个bean
			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;
	}

isCandidateComponent该方法执行完之后如果为true表示是一个bean则会进入if逻辑里面,首先的话主要是设置bean的部分属性,然后调用isCandidateComponent(sbd)方法判断是否是接口抽象类...注意这两个isCandidateComponent穿的参数不一样执行逻辑也不一样

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		//不是独立的类不能成为bean,接口或者抽象类不能成为bean,但抽象类若有Lookup注解则可以成为bean
		//Lookup注解:比如UserService里面依赖注入了一个user,该user是prototype,UserService里面有一个test方法输出user
		//最终打印出来的是同一个,因为UserService是单例的,此时user加了Lookup注解的话就能达到我们预期的效果
		return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
	}

若返回是true时就直接将其加入BeanDefinition集合里面返回candidates.add(sbd);

最终findCandidateComponents方法就是返回一个BeanDefinition集合,然后接下来就是遍历这个集合,对集合中的BeanDefinition进行处理,比如设置scope注解信息,beanName,解析@Lazy、@Primary、@DependsOn、@Role、@Description注解

Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			//遍历BeanDefinition
			for (BeanDefinition candidate : candidates) {
				//解析scope注解信息
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				//设置scope注解信息
				candidate.setScope(scopeMetadata.getScopeName());
				主要是解析Component有没有设置bean的名字,有则直接返回,
				// 没有则根据短名构造一个(如果写的名字第1、2个字符是大写则直接返回,否则直接将第一个字符转成小写返回)
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				if (candidate instanceof AbstractBeanDefinition) {
					//主要是给BeanDefinition设置一些默认的值
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					// 解析@Lazy、@Primary、@DependsOn、@Role、@Description注解,为这些注解设置值
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
generateBeanName实则就是设置bean的名字,其逻辑大致是:解析Component有没有设置bean的名字,有则直接返回, 没有则根据短名构造一个(如果写的名字第1、2个字符是大写则直接返回,否则直接将第一个字符转成小写返回)
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
		if (definition instanceof AnnotatedBeanDefinition) {
			// 获取注解所指定的beanName
			String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
			//有指定的则直接返回
			if (StringUtils.hasText(beanName)) {
				// Explicit bean name found.
				return beanName;
			}
		}
		// Fallback: generate a unique default bean name.
		//没有指定的则根据类的短名生成默认的名字
		return buildDefaultBeanName(definition, registry);
	}

设置完bean的名字之后就是调用postProcessBeanDefinition方法给bean赋一些默认值,然后调用processCommonDefinitionAnnotations解析处理一些注解

	if (candidate instanceof AbstractBeanDefinition) {
					//主要是给BeanDefinition设置一些默认的值
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					// 解析@Lazy、@Primary、@DependsOn、@Role、@Description注解,为这些注解设置值
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}

        最后就是检查Spring容器中是否已经存在该beanName,有的话会先判断是否兼容,就是对于的路径resource是不是一样的,匹配则不会加入spring容器不匹配则抛出异常,没有则直接加入spring容器

// 检查Spring容器中是否已经存在该beanName,有的话会判断是否兼容,兼容则不会加入spring容器
				//不兼容则抛出异常,没有则直接加入spring容器
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);

					// 注册
					registerBeanDefinition(definitionHolder, this.registry);
				}

通过checkCandidate方法判断

protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
		//容器里面没有该BeanDefinition
		if (!this.registry.containsBeanDefinition(beanName)) {
			return true;
		}
		//检查是否以及有该BeanDefinition
		BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
		BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
		if (originatingDef != null) {
			existingDef = originatingDef;
		}
		// 是否兼容,如果兼容返回false表示不会重新注册到Spring容器中,如果不冲突则会抛异常。
		if (isCompatible(beanDefinition, existingDef)) {
			return false;
		}
		throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
				"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
				"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
	}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鹏之翼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值