CandidateComponentsIndexLoader 被 final 修饰,也不允许实例化,提供 loadIndex(@Nullable ClassLoader classLoader) 静态方法,获取所有 META-INF/spring.components 文件中的内容,存在文件并包含内容则创建对应的 CandidateComponentsIndex 对象
整过过程不复杂,如下:
根据 spring.index.ignore 变量判断是否需要忽略本次加载过程,默认为 false
获取所有的 META-INF/spring.components 文件
加载出所有 META-INF/spring.components 文件的内容,并生成多个 key-value
内容不为空则创建对应的 CandidateComponentsIndex 对象返回
例如 META-INF/spring.components 文件这样配置:
example.scannable.AutowiredQualifierFooService=example.scannable.FooService example.scannable.DefaultNamedComponent=org.springframework.stereotype.Component example.scannable.NamedComponent=org.springframework.stereotype.Component example.scannable.FooService=example.scannable.FooService example.scannable.FooServiceImpl=org.springframework.stereotype.Component,example.scannable.FooService example.scannable.ScopedProxyTestBean=example.scannable.FooService example.scannable.StubFooDao=org.springframework.stereotype.Component example.scannable.NamedStubDao=org.springframework.stereotype.Component example.scannable.ServiceInvocationCounter=org.springframework.stereotype.Component example.scannable.sub.BarComponent=org.springframework.stereotype.Component生成的 CandidateComponentsIndex 对象如下所示:
3. findCandidateComponents 方法findCandidateComponents(String basePackage) 方法,解析出包路径下所有符合条件的 BeanDefinition,方法如下:
public Set<BeanDefinition> findCandidateComponents(String basePackage) { /* * 2. 扫描包路径,通过 ASM(Java 字节码的操作和分析框架)解析出符合条件的 AnnotatedGenericBeanDefinition 们,并返回 * 说明: * 针对 `1` 解析过程中去扫描指定路径下的 .class 文件的性能问题,从 Spring 5.0 开始新增了一个 @Indexed 注解(新特性), * @Component 注解上面就添加了 @Indexed 注解 * * 这里不会去扫描指定路径下的 .class 文件,而是读取所有 `META-INF/spring.components` 文件中符合条件的类名, * 直接添加 .class 后缀就是编译文件,而不要去扫描 * * 没在哪看见这样使用过,可以参考 ClassPathScanningCandidateComponentProviderTest#customAnnotationTypeIncludeFilterWithIndex 测试方法 */ if (this.componentsIndex != null // `componentsIndex` 不为空,存在 `META-INF/spring.components` 文件并且解析出数据则会创建 && indexSupportsIncludeFilters()) // `includeFilter` 过滤器的元素(注解或类)必须标注 @Indexed 注解 { return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { /* * 1. 扫描包路径,通过 ASM(Java 字节码的操作和分析框架)解析出符合条件的 ScannedGenericBeanDefinition 们,并返回 * 首先需要去扫描指定路径下所有的 .class 文件,该过程对于性能有不少的损耗 * 然后通过 ASM 根据 .class 文件可以获取到这个类的所有元信息,也就可以解析出对应的 BeanDefinition 对象 */ return scanCandidateComponents(basePackage); } }这个方法的实现有两种方式,都是基于 ASM(Java 字节码的操作和分析框架)实现的,默认情况下都是第 1 种,分别如下:
1,调用 scanCandidateComponents(String basePackage) 方法,默认
扫描包路径,通过 ASM(Java 字节码的操作和分析框架)解析出符合条件的 ScannedGenericBeanDefinition 们,并返回。首先需要去扫描指定路径下所有的 .class 文件,该过程对于性能有不少的损耗;然后通过 ASM 根据 .class 文件可以获取到这个类的所有元信息,也就可以解析出对应的 BeanDefinition 对象