死磕Spring之IoC篇 - BeanDefinition 的解析过程(面向注解) (6)

addCandidateComponentsFromIndex(CandidateComponentsIndex index, String basePackage) 方法,根据 META-INF/spring.components 文件,获取带有 @Indexed 注解的类名,然后解析出符合条件的 BeanDefinition,方法如下:

private Set<BeanDefinition> addCandidateComponentsFromIndex(CandidateComponentsIndex index, String basePackage) { // <1> 定义 `candidates` 用于保存符合条件的 BeanDefinition Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { Set<String> types = new HashSet<>(); // <2> 根据过滤器从所有 `META-INF/spring.components` 文件中获取所有符合条件的**类名称** for (TypeFilter filter : this.includeFilters) { // <2.1> 获取过滤注解(或类)的名称(例如 `org.springframework.stereotype.Component`) String stereotype = extractStereotype(filter); if (stereotype == null) { throw new IllegalArgumentException("Failed to extract stereotype from " + filter); } // <2.2> 获取注解(或类)对应的条目,并过滤出 `basePackage` 包名下的条目(类的名称) types.addAll(index.getCandidateTypes(basePackage, stereotype)); } boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); // <3> 开始对第 `2` 步过滤出来类名进行处理,符合条件的类名会解析出一个 AnnotatedGenericBeanDefinition for (String type : types) { // <3.1> 根据这个类名找到 `.class` 文件,通过 ASM(Java 字节码操作和分析框架)获取这个类的所有信息 // `metadataReader` 对象中包含 ClassMetadata 类元信息和 AnnotationMetadata 注解元信息 // 也就是说根据 `.class` 文件就获取到了这个类的元信息,而不是在 JVM 运行时通过 Class 对象进行操作,提高性能 MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(type); // <3.2> 根据所有的过滤器判断这个类是否符合条件(例如必须标注 @Component 注解或其派生注解) if (isCandidateComponent(metadataReader)) { // <3.3> 如果符合条件,则创建一个 AnnotatedGenericBeanDefinition 对象 AnnotatedGenericBeanDefinition sbd = new AnnotatedGenericBeanDefinition( metadataReader.getAnnotationMetadata()); /* * <3.4> 再次判断这个类是否符合条件(不是内部类并且是一个具体类) * 具体类:不是接口也不是抽象类,如果是抽象类则需要带有 @Lookup 注解 */ if (isCandidateComponent(sbd)) { // <3.5> 符合条件,则添加至 `candidates` 集合 candidates.add(sbd); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } // <4> 返回 `candidates` 集合 return candidates; }

过程如下:

定义 candidates 用于保存符合条件的 BeanDefinition

根据过滤器从所有 META-INF/spring.components 文件中获取所有符合条件的类名称

获取过滤注解(或类)的名称(例如 org.springframework.stereotype.Component)

获取注解(或类)对应的条目,并过滤出 basePackage 包名下的条目(类的名称)

开始对第 2 步过滤出来类名进行处理,符合条件的类名会解析出一个 AnnotatedGenericBeanDefinition

根据这个类名找到 .class 文件,通过 ASM(Java 字节码操作和分析框架)获取这个类的所有信息,生成 metadataReader 对象。这个对象其中包含 ClassMetadata 类元信息和 AnnotationMetadata 注解元信息,也就是说根据 .class 文件就获取到了这个类的元信息,而不是在 JVM 运行时通过 Class 对象进行操作,提高性能

根据所有的过滤器判断这个类是否符合条件(例如必须标注 @Component 注解或其派生注解)

如果符合条件,则创建一个 AnnotatedGenericBeanDefinition 对象

再次判断这个类是否符合条件(不是内部类并且是一个具体类),具体类:不是接口也不是抽象类,如果是抽象类则需要带有 @Lookup 注解

符合条件,则添加至 candidates 集合

返回 candidates 集合

该过程不会去扫描到所有的 .class 文件,而是从 META-INF/spring.components 文件中读取,知道了类名称也就知道了 .class 文件的路径,然后可以通过 ASM 进行操作了。Spring 5.0 开始新增的一个 @Indexed 注解(新特性),目的为了提高性能。

总结

本文面向注解(@Component 注解或其派生注解)定义的 Bean,Spring 是如何将他们解析成 BeanDefinition(Bean 的“前身”)并注册的,大致过程如下:

ClassPathBeanDefinitionScanner 会去扫描到包路径下所有的 .class 文件

通过 ASM(Java 字节码操作和分析框架)获取 .class 对应类的所有元信息

根据元信息判断是否符合条件(带有 @Component 注解或其派生注解),符合条件则根据这个类的元信息生成一个 BeanDefinition 进行注册

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywzdd.html