Spring 扫描原理源码分析

Spring 扫描原理源码分析

第一次写文章,若有错请指正

spring容器启动

众所周知spring启动得实例化一个容器xxxApplicationContext。实例化有着4种构造方法,其中与扫描有关的是下面两种传入一个类一段字符串

	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		this();
		register(componentClasses);
		refresh();
	}
	
	public AnnotationConfigApplicationContext(String... basePackages) {
		this();
		scan(basePackages);
		refresh();
	}

两种实例化方法就只有一行代码不同,本文先介绍第一种实例化方法,传入一个类的。

register(componentClasses) 注册一个类

这行代码主要就是把传进来的类解析成BeanDefinition并且添加到BeanDefinitionMap当中。

	//删除了不相关的代码
	public void register(Class<?>... componentClasses) {
		this.reader.register(componentClasses);
	}
	------------------------------
	public void register(Class<?>... componentClasses) {
		for (Class<?> componentClass : componentClasses) {
			registerBean(componentClass);
		}
	}
	------------------------------
	public void registerBean(Class<?> beanClass) {
		doRegisterBean(beanClass, null, null, null, null);
	}
	------------------------------
	private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
			@Nullable BeanDefinitionCustomizer[] customizers) {
		//把传进来的类转换成bd对象
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		abd.setInstanceSupplier(supplier);
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		abd.setScope(scopeMetadata.getScopeName());
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
		//下面就是设置bd的属性了
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		if (qualifiers != null) {
			for (Class<? extends Annotation> qualifier : qualifiers) {
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				else {
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}
		if (customizers != null) {
			for (BeanDefinitionCustomizer customizer : customizers) {
				customizer.customize(abd);
			}
		}
		//在这里把bd封装成bdHolder对象方便注册到工厂的Map当中
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}
	----------------
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {
		// Register bean definition under primary name. 在工厂里注册bean definition
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
	}
	--------------
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
		//一般来讲 从Map中取得的是空的 要是不为空的话经过一系列的判断,最后还是会把新的放着Map中
		BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
		if (existingDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
				throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
			}
			else if (existingDefinition.getRole() < beanDefinition.getRole()) {
			//省略了日志输出
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
			//判断存放已经创建Bean的Set集合是不是空的 一般来讲还是为空的,因为这个时候spring容器还没有刷新
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
				synchronized (this.beanDefinitionMap) {
					//这一步就是把bd放在Map当中
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					//替换bdNames的List集合
					this.beanDefinitionNames = updatedDefinitions;
					removeManualSingletonName(beanName);
				}
			}
			else {
				// Still in startup registration phase
				this.beanDefinitionMap.put(beanName, beanDefinition);
				this.beanDefinitionNames.add(beanName);
				removeManualSingletonName(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}
	}

上面的代码就是把一个类转换成bd然后放到bdMap当中,这一步至关重要。执行完后register后则是Spring容器的刷新流程了,实际扫描的入口是在refresh()的invokeBeanFactoryPostProcessors(beanFactory) 这个方法,执行Bean工厂的后置处理器

Bean工厂的后置处理器

Bean工厂的后置处理器有很多,也可以自定义,本文不多介绍,只讲解使用的。
与spring扫描相关的后置处理器是ConfigurationClassPostProcessor这个类,这个类是spring内置的,实现了BeanDefinitionRegistryPostProcessor这个接口。

	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
		}
	}
	-----------------------
	public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
			List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

			//  这里就是从bd中获取实现了BeanDefinitionRegistryPostProcessor这个接口的类
			String[] postProcessorNames =
					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
			for (String ppName : postProcessorNames) {
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
					//从容器中获取这个Bean 并且加入到一个集合当中
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					processedBeans.add(ppName);
				}
			}
			sortPostProcessors(currentRegistryProcessors, beanFactory);
			registryProcessors.addAll(currentRegistryProcessors);
			执行postProcessBeanDefinitionRegistry方法
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
			currentRegistryProcessors.clear();
	}
	------------------------
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		//调用这个方法
		processConfigBeanDefinitions(registry);
	}

上面的都是一些处理流程,最终执行的方法是processConfigBeanDefinitions这个方法。这个方法尤为重要,就是这个方法处理了扫描的。

checkConfigurationClassCandidate 检查是不是一个配置类

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		String[] candidateNames = registry.getBeanDefinitionNames();
		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			//这里主要是判断是不是一个配置类
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}
	}

上面的代码是从工厂的bdMap中遍历,找到是配置类的bd然后添加到configCandidates这个配置类候选者的集合里面。

	public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
		//先从元数据中获取@Configuration这个注解的信息
		Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
		//config不为空且里面的一个属性的值不为false,就把这个bd称为一个完整的(full)的配置类
		if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
		}
		//上面的条件不符合就来到这里,判断是不是一个lite的配置类。要是上下两个判断都不符合就不是一个配置类。
		else if (config != null || isConfigurationCandidate(metadata)) {
			beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
		}
		else {
			return false;
		}
	}
	--------------------------------------------
	public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
		// 这里就是判断是不是一个lite的配置类了,candidateIndicators包含着@Component、@ComponentScan、@Import、@ImportResource这4个注解
		for (String indicator : candidateIndicators) {
			if (metadata.isAnnotated(indicator)) {
				return true;
			}
		}
	}
	--------------
	static {
		candidateIndicators.add(Component.class.getName());
		candidateIndicators.add(ComponentScan.class.getName());
		candidateIndicators.add(Import.class.getName());
		candidateIndicators.add(ImportResource.class.getName());
	}

在这个方法里面最关键的就是这几行代码,只有Spring识别到你是一个配置类才会把你进行解析的,至于full和lite的区别可以看这篇博文

parser.parse(candidates);

这里还算不上扫描的入口,还是在解析着上面通过筛选的配置类。就是实例化一个配置类的解析对象(ConfigurationClassParser)来对bd进行解析

ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);

		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
		Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
		do {
			StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
			//解析
			parser.parse(candidates);
			parser.validate();
	--------------------------------------
	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();
			//这里就是对这些bd分别解析
			try {
				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				}
				else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				}
				else {
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			}
		this.deferredImportSelectorHandler.process();
	}
	------------------------------

将bd封装成一个ConfigurationClass对象

之所以封装成一个ConfigurationClass对象是因为要把解析注解得到的数据给存起来,方便在下面注册给Spring容器中的工厂。

	protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
		//封装成一个ConfigurationClass对象
		processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
	}
	-----------------
	//最终一个注解形式的配置类bd的解析入口是
	do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
		}

doProcessConfigurationClass Spring基本注解解析

这个方法非常长,可是作用也非常大。主要是对
1.@PropertySource
2.@ComponentScan
3.@Import
4.@ImportResource
5.@Bean
6.重写接口的方法
7.进程超类的方法
对这7个注解和方法进行解析,本文着重讲解@ComponentScan这个注解的解析,这个注解正是扫描的关键。

protected final SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {
		// 处理@ComponentScan注解
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// 这个parser就是将标注类上的@ComponentScan进行解析
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// 这里是对扫描得到的bd进行解析,类似上面的步骤。
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}
	}
	

要特别注意的是,在this.componentScanParser.parse()这个方法里有一个重要的步骤,先是new出一个扫描的对象,而这个扫描的对象是默认把@Component给扫描到的。

ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
				componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
-----------------------
	//实例化ClassPathBeanDefinitionScanner 对象
	public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {
		//useDefaultFilters这个值在@ComponentScan注解上缺省值的true的,所以会把含有@Component注解的类给扫描到。
		if (useDefaultFilters) {
			registerDefaultFilters();
		}
	}
	------------------
	protected void registerDefaultFilters() {
		this.includeFilters.add(new AnnotationTypeFilter(Component.class));
		..........................
	}

在扫描前对@ComponentScan传入的包名进行获取

		//演示类
		@ComponentScan("com.zsk")
		public class TestMain {

		}
		//找到注解中的要扫描的包的值
		Set<String> basePackages = new LinkedHashSet<>();
		String[] basePackagesArray = componentScan.getStringArray("basePackages");
		for (String pkg : basePackagesArray) {
			String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
					ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
			Collections.addAll(basePackages, tokenized);
		}
		for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
			basePackages.add(ClassUtils.getPackageName(clazz));
		}
		if (basePackages.isEmpty()) {
			basePackages.add(ClassUtils.getPackageName(declaringClass));
		}
		//扫描入口
		return scanner.doScan(StringUtils.toStringArray(basePackages));

通过这个扫描的对象调用doScan方法,进行Spring类的扫描,将符合规则的类给扫描进Spring容器。同样调用doScan方法的还有上面提到过的实例化Spring容器的构造方法在这里插入图片描述
只不过这里是直接传入扫描的路径,与传入一个类不同的是,没有提前注册一个类到Spring容器当中。
回到doScan方法当中,真正进行扫描的是这个方法scanCandidateComponents

	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) {
			/**
			 * 找到包名下加了@Component注解的类 解析成为BeanDefinition
			 */
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
	---------------------
	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			//就是这个方法
			return scanCandidateComponents(basePackage);
		}
	}
	---------------------
	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
		//示例:比如我扫描包的路径是com.zsk 那么转换后的格式是 “classpath*:com/zsk/**/*.class”
			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.setSource(resource);
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);
							}
		return candidates;
	}
	---------------------

上面的代码是把包下全部的类都给获取得到了,接下来就是要进行筛选,把在类上标注了@Component注解的类个添加到一个集合里面。调用isCandidateComponent进行判断,isCandidateComponent()这个方法会调用两次;第一次调用这个方法是先判断是否和excludeFilters相符的类,然后判断该类是否是应该跳过的类。第二次调用是判断是否是一个独立和具体的类都true的话就把产生的bd放进一个Set集合里面传出去,这个方法的路程走完了:findCandidateComponents(basePackage);

	//第一次调用
	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);
		-----------------------
	private boolean isConditionMatch(MetadataReader metadataReader) {
		return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
	}
		-----------------------
			}
		}
		return false;
	}

接着把符合条件的类转化为一个Bean Definition对象后放在一个集合里面,传回出去scanCandidateComponents这个方法,对符合条件的bd对象赋值,然后注册到工厂的bdMap中。

for (String basePackage : basePackages) {
			/**
			 * 找到包名下加了@Component注解的类 解析成为BeanDefinition
			 */
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			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);
				}
				/**
				 * 设置BeanDefinition的属性
				 */
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				/**
				 * 注册到工厂的BeanDefinitionMap中
				 */
				if (checkCandidate(beanName, candidate)) {
					//封装成一个bdHolder对象传递
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}

doScan方法就这样完成了,回到解析@ComponentScan注解的代码上,接下来就是对得到的bd集合进行类似的解析。

		// Process any @ComponentScan annotations  处理@ComponentScan注解
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			for (AnnotationAttributes componentScan : componentScans) {
				// 得到符合扫描条件得bd对象
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
				// 对bd对象进行解析
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

完成doProcessConfigurationClass方法,把封装的ConfigurationClass对象给存储进来。

		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
		}
		while (sourceClass != null);
		//存放到configurationClasses中
		this.configurationClasses.put(configClass, configClass);

loadBeanDefinitions 加载BeanDefinitions

回到一开始的processConfigBeanDefinitions方法,接下来就是对封装的ConfigurationClass对象进行处理,加载到Spring容器中。

	//得到ConfigurationClass的集合
	Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);

			// Read the model and create bean definitions based on its content
			if (this.reader == null) {
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
			/**
			 * 加载从配置类扫描到的@Import、@Bean方法、@ImportResource等 注册到BeanDefinitionMap中
			 */
			this.reader.loadBeanDefinitions(configClasses);

Spring扫描到这里算是完成了,下面的代码是对上面扫描解析进行校验,看看是不是把扫描得到得配置类都给解析完了

			/**
			 * 下面是对扫描前和扫描后的bdMap进行对比
			 */
			candidates.clear();
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<>();
				//把上面解析完的类添加到alreadyParsedClasses--已经解析完的类
				for (ConfigurationClass configurationClass : alreadyParsed) {
					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
				}
				for (String candidateName : newCandidateNames) {
					//判断是不是新的bd
					if (!oldCandidateNames.contains(candidateName)) {
						BeanDefinition bd = registry.getBeanDefinition(candidateName);
						//判断bd是否是候选的配置类并且是不是已经解析完的类
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(bd.getBeanClassName())) {
							//添加到candidates再次解析
							candidates.add(new BeanDefinitionHolder(bd, candidateName));
						}
					}
				}
				//新的bdMap就变成旧的bdMap
				candidateNames = newCandidateNames;
			}
		}
		while (!candidates.isEmpty());

至此,Spring得扫描就到此完成了。总的来说,Spring得扫描就是通过得到包得路径然后扫描包下每一个类,得到类的元数据。通过判断是否是一个配置类来进行解析。
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值