利用Spring Bean怎么实现一个包扫描功能
创始人
2024-12-28 09:41:20
0次

首先,找到@Component注解的处理类

注解的定义,一般都需要配套的对注解的处理才能完成注解所代表的功能。所以我们通过@Component注解的用到的地方,来查找可能的处理逻辑;
我们先进入Spring的项目,在IDEA里面用Ctrl和鼠标左键点击Component注解的名称,IDEA会显示出使用到这个类的位置,我们从弹出的列表中找到一个名称像的类,去看类上面的注释说明,如图:

利用Spring Bean怎么实现一个包扫描功能

我们点进类中,可以看到第一行就说了这个类是为了从classpath里面找到定义的Bean:

利用Spring Bean怎么实现一个包扫描功能

分析具体方法

一般Spring的类都是经过设计的,职责清晰。所以一般都是有简单直接的接口暴露,我们打开类的公开API可以看到有个很直接的方法就叫做扫描,看看注释说“从指定的包中扫描Bean”,那就是它了。

利用Spring Bean怎么实现一个包扫描功能

然后,我们为了确认,实现确实是通过这个方法,可以启动程序,打个断点看看是否经过这里(但是这这里,没有调用scan()方法,而是更深一层的doScan方法,也确实费解)。

我们进入doScan() 方法看看实现:

protected Set doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set beanDefinitions = new LinkedHashSet<>();
		// 可以指定多个basePackage,这里就对每个都处理
		for (String basePackage : basePackages) {
		  // 这个方法是真正的查找候选Bean的地方
			Set candidates = findCandidateComponents(basePackage);
			// 对于每个查找出的候选Bean,进行处理
			for (BeanDefinition candidate : candidates) {
			  // 解析@Scope的元数据
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				// 为候选的Bean生成一个名称
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				// 应用后置处理器
				if (candidate instanceof AbstractBeanDefinition) {
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				// 
				// 处理一些其它通用的注解的元数据
				if (candidate instanceof AnnotatedBeanDefinition) {
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				// 校验通过后,注册到 BeanFactory
				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;
	}

从方法中我们可以明显的看到,核心代码还在findCandidateComponents方法里面,我们进入这个方法后再通过调试一直找到核心代码scanCandidateComponents。如下图,第一处是找到指定包路径所代表的classpath中的资源对象, 但是这里只是找到了包下面有什么,但是还不知道包下面的类是不是一个候选的Bean(可以看到将DTO类也扫描到了)。如下:

利用Spring Bean怎么实现一个包扫描功能

正常思路,拿到了有哪些资源就该进一步去筛选,看看这些资源有哪些是真正的Bean的定义类。

现在我们还不清楚的是,Spring通过什么方式知道一个类是否是真正的Bean的。我们继续调试,到上图的430行debug进去看看,可以走到org.springframework.core.type.classreading.SimpleMetadataReader这个类的构造器中,如下:

SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
    // 通过流读取资源的内容,现在这个资源可以认为是我们的类
		InputStream is = new BufferedInputStream(resource.getInputStream());
		ClassReader classReader;
		try {
		  // 这个Reader的构造器中就将流读取完毕了
			classReader = new ClassReader(is);
		}
		catch (IllegalArgumentException ex) {
		  // 通过这个异常的信息,可以推测出,其实这里是通过ASM读取Class文件的定义了
			throw new NestedIOException("ASM ClassReader failed to parse class file - " +
					"probably due to a new Java class file version that isn't supported yet: " + resource, ex);
		}
		finally {
			is.close();
		}

    // 这里根据命名可以推测是访问者模式来暴露注解的元数据
		AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);

		// 这个accpect方法也是访问者模式中的典型方法,在这里面,是数据的解析逻辑
		classReader.accept(visitor, ClassReader.SKIP_DEBUG);

		this.annotationMetadata = visitor;
		// (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
		this.classMetadata = visitor;
		this.resource = resource;
	}

我们在进入classReader.accept方法,这里面可以看到reader对于Class文件的的按字节解析。

利用Spring Bean怎么实现一个包扫描功能

例如,下面读取的类声明,类注解都是包扫描需要的类元数据:

利用Spring Bean怎么实现一个包扫描功能

拿到这些元数据之后,就按照包扫描的过滤器就过滤出真正需要的类,作为候选的Bean

利用Spring Bean怎么实现一个包扫描功能

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...