spring-源码解析(扫描)

spring源码解析-扫描

相信这双手,无论何时,都有力量把自己从任何处境中拉出来,凡是自强不息者,我辈皆能自救。



前言

spring扫描这部分相对来说还是比较简单的,但是也有一些比较有意思的点,例如spring扫描的时候竟然也有索引,这个是不是没有想到,见证奇迹的时候到了


一、扫描?

spring中的扫描就像是雷达,我们想干掉敌机(彻底理解spring)那么就应该先找到他的位置。

二、扫描逻辑

1.入口

代码如下(示例):

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

我们点进去AnnotationConfigApplicationContext这个方法,进入到以下方法:

public AnnotationConfigApplicationContext() {
		StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
		this.reader = new AnnotatedBeanDefinitionReader(this);
		createAnnotatedBeanDefReader.end();
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

解析:
this.reader = new AnnotatedBeanDefinitionReader(this);
这行代码其实就是创建了个BeanDfinition的读取器,其实他是读取的但是并不是扫描的真正的扫描是
this.scanner = new ClassPathBeanDefinitionScanner(this);
ClassPathBeanDefinitionScanner 这个类里面有一个scan的方法其中调用了doscan方法就是spring的扫描逻辑

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);
		}

		//看看扫描出来多少bean
		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}
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);
			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) {
					// 解析@Lazy、@Primary、@DependsOn、@Role、@Description
					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;
	}

首先得到一个basePackages是一个包路径类似于com.mo.yu这种形式的,然后去遍历所有的你定义的包路径一个一个进行扫描,然后会进入findCandidateComponents 这个方法

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		//这个方法是索引方法 相当于你要在配置一个springcomponents文件用kv的形式写上,k包路径v那个注解,但是其实没有什么卵用
		//就是扫描的时候速度稍微快一点不用循环,而是直接读取的这个配置文件里面的东西。spring 考虑的真周到TMD。
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			//一般都是直接进入这个方法
			return scanCandidateComponents(basePackage);
		}
	}

我们一般都是会直接进入scanCandidateComponents这个方法中的但是如果你要是配置了springcomponents 文件就会进入if条件中,这个方法其实没啥卵用,有兴趣的同学可以进去看看。

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 {
						//元数据读取器  可以读取类的信息 例如类的名字,注解等  底层使用的是ASM技术
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						// excludeFilters、includeFilters判断  看看是否需要排除
						if (isCandidateComponent(metadataReader)) {
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setSource(resource);
							//看看是不是加了Component 注解
							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;
	}

这边他会把com.mo.yu这样的路径转换成
classpath*:com/mo/yu/**/*.class这样的路径,通过这个路径其实是得到了一个file的一个文件,也就是这个包下所有的文件,然后进行遍历

MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);

这行代码其实是他new了一个元数据读取器,可以理解成一个工具类,它可以读取类的信息,类里面的注解,已经里面的抽象类。
接下来回去判断你的注解上加没加一些排除的类,在下面的时候就会给你排除掉
在这里插入图片描述看看是否加了Component注解
这个犯法这个方法会判断你是不是接口是不是抽象类加了没有加Lookup注解
经一系列判断就会返回出去

在这里插入图片描述
解析出来bean的类型多例的还是单例的
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
解析是否是懒加载等这些注解
然后最终注册到beanDefinitionMap中去


总结

等再次摸鱼的时候来一个总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值