当前位置:   article > 正文

1、SpringBoot解析启动类之源码分析_configurationclassutils.checkconfigurationclasscan

configurationclassutils.checkconfigurationclasscandidate

解析启动类流程主要分为三大块:解析类路径下用户自定义的所有类、解析SPI机制从spring.factories文件中加载获取到的类、以及创建所有候选类的BeanDefinitions「用户自定义的类在扫描包路径时触发BeanDefinitions的初始化、spring.factories文件中的候选类以及其间接加载的候选类单独触发BeanDefinitions的初始化」。

  1. 先注册用户自定义类其BeanDefinitions至bean 工厂中。
  2. 再次通过ImportSelector延迟类型之DeferredImportSelector利用SPI手段加载得到全部spring.factories文件中配置的全部候选类。
  3. 遍历步骤2每一个候选类,依次分析其存在的@Component、@Import、@Bean等相关注解再次导入的候选类。
  4. 注册步骤2、步骤3的候选类其BeanDefinitions至bean 工厂中。


  1. public class ConfigurationClassPostProcessor{
  2. public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
  3. ...
  4. do {
  5. //ConfigurationClassParser#parse
  6. parser.parse(candidates);
  7. parser.validate();
  8. ...
  9. }
  10. ...
  11. // ===================== 注册自动装配候选类以及其@bean注解对应BeanDefinition至bean工厂中
  12. // 参考第7节
  13. this.reader.loadBeanDefinitions(configClasses);
  14. while (!candidates.isEmpty());
  15. }
  16. }

AnnotatedGenericBeanDefinition类型的BeanDefinition。这种类型的BeanDefinition提供基于元数据、元注解分析的StandardAnnotationMetadata。

由于SpringBoot启动过程中存在诸多元数据、元注解的解析,其中AnnotatedGenericBeanDefinition类型的BeanDefinition初始化了ClassMetadata、AnnotatedTypeMetadata两种类型解析器StandardAnnotationMetadata

ImportSelector延迟类型之DeferredImportSelector:加载类路径下自动装配的全部候选类,并且解析每一个候选类是否存在@Component、@ComponentScans、@Import等相关注解。

  1. class ConfigurationClassParser {
  2. private final DeferredImportSelectorHandler deferredImportSelectorHandler = new DeferredImportSelectorHandler();
  3. public void parse(Set<BeanDefinitionHolder> configCandidates) {
  4. for (BeanDefinitionHolder holder : configCandidates) {
  5. BeanDefinition bd = holder.getBeanDefinition();
  6. try {
  7. if (bd instanceof AnnotatedBeanDefinition) {
  8. parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
  9. }
  10. ...
  11. }
  12. }
  13. //【自定义类处理完毕】===========================延迟类型的ImportSelector处理器
  14. this.deferredImportSelectorHandler.process();
  15. }
  16. //metadata:StandardAnnotationMetadata
  17. protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
  18. processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
  19. }
  20. protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
  21. if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
  22. return;
  23. }
  24. // 此处存在去重逻辑,避免重复处理候选类
  25. ConfigurationClass existingClass = this.configurationClasses.get(configClass);
  26. ...
  27. // Recursively process the configuration class and its superclass hierarchy.
  28. SourceClass sourceClass = asSourceClass(configClass, filter);
  29. do {
  30. sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
  31. }
  32. while (sourceClass != null);
  33. this.configurationClasses.put(configClass, configClass);
  34. }
  35. @Nullable
  36. protected SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter){
  37. if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
  38. // Recursively process any member (nested) classes first
  39. processMemberClasses(configClass, sourceClass, filter);
  40. }
  41. // Process any @PropertySource annotations
  42. ...
  43. // Process any @ComponentScan annotations
  44. AnnotationMetadata am = sourceClass.getMetadata();
  45. Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(am, ComponentScans.class, ComponentScan.class);
  46. if (!componentScans.isEmpty() && !conditionEvaluator.shouldSkip(am, ConfigurationPhase.REGISTER_BEAN)) {
  47. for (AnnotationAttributes componentScan : componentScans) {
  48. String className = sourceClass.getMetadata().getClassName();
  49. //==========================================将扫描类路径下得到的用户自定义类的BeanDefinition注册到 bean工厂中
  50. Set<BeanDefinitionHolder> scannedBeanDefinitions = componentScanParser.parse(componentScan, className);
  51. for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
  52. BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
  53. if (bdCand == null) {
  54. bdCand = holder.getBeanDefinition();
  55. }
  56. if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
  57. // 解析每个自定义类存在的@Component、@PropertySource、@ComponentScan...等注解
  58. parse(bdCand.getBeanClassName(), holder.getBeanName());
  59. }
  60. }
  61. }
  62. }
  63. // Process any @Import annotations
  64. processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
  65. // Process any @ImportResource annotations
  66. // Process individual @Bean methods
  67. Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
  68. for (MethodMetadata methodMetadata : beanMethods) {
  69. configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
  70. }
  71. return null;
  72. }
  73. }

configCandidates:AnnotatedBeanDefinition类型的启动类。

shouldSkip:判断当前候选类是否存在Conditional注解。正常情况下启动类不存在的。

最终结果:符合条件的类最终都会添加到Map类型的集合ConfigurationClasses中。

涉及的注解解析流程如下:

  1. 解析@Conditional注解。
  2. 解析@Component注解。
  3. 解析@PropertySources注解。
  4. 解析@ComponentScans注解。
  5. 解析@Import注解。
  6. 解析@ImportResource注解。
  7. 解析@Bean注解。

SpringBoot中解析任何一个类「包括上述注解解析到的类」都会执行ConfigurationClassParser#processConfigurationClass方法。 就是因为每个候选类最终都会作为Map类型的集合ConfigurationClasses中元素。

deferredImportSelectorHandler.process:解析核心类AutoConfigurationImportSelector过程中利用SPI机制加载spring.factories文件中配置的候选的装配类。 这些装配类的解析同样会使用当前文的全文逻辑。具体参考


1、解析@Conditional注解

 该接口的实现类【OnBeanCondition、OnClassCondition、OnPropertyCondition】等都存在一个用于排序的注解`@Order`。

@Order注解其value取值越大对应排序时的优先级越低。

使用方式之直接方式即`自定义实现接口Condition的子类 | 直接@Conditional`,间接方式之@Conditional

  • @ConditionalOnBean【@Conditional(OnBeanCondition.class)】。
  • @ConditionalOnMissingBean【@Conditional(OnBeanCondition.class)】。
  •  @ConditionalOnMissingClass【@Conditional(OnClassCondition.class)】。
  •  @ConditionalOnClass【@Conditional(OnClassCondition.class)】。
  •  @ConditionalOnProperty【@Conditional(OnPropertyCondition.class)】。

通过组合注解方式使用时其@Conditional注解属性class【OnBeanCondition、OnClassCondition、OnPropertyCondition】都会对应的赋值实现接口Condition的子类。

作用:主要是利用接口Condition的功能判断【直接方式、间接方式】配置类是否满足条件。

主要解析的是当前类的元注解信息「AnnotatedTypeMetadata」。

  • 如果当前类不存在@Conditional相关的注解,则返回false。
  • 如果存在则根据Condition接口相关判定条件裁决当前类是否允许添加到Map类型的集合ConfigurationClasses中。

如果返回true,当前类则不会添加到集合ConfigurationClasses中,也即Spring IOC容器中更不会存在。

如果这些注解存在于类内部的方法上【通常是协同@Bean注解】,符合条件的会将@Bean的bean定义信息添加到beanDefinitionMap。后续用于实例并初始化。

  1. class ConditionEvaluator {
  2. public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
  3. if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
  4. return false;
  5. }
  6. ...
  7. List<Condition> conditions = new ArrayList<>();
  8. for (String[] conditionClasses : getConditionClasses(metadata)) {
  9. for (String conditionClass : conditionClasses) {
  10. Condition condition = getCondition(conditionClass, this.context.getClassLoader());
  11. conditions.add(condition);
  12. }
  13. }
  14. AnnotationAwareOrderComparator.sort(conditions);
  15. for (Condition condition : conditions) {
  16. ConfigurationPhase requiredPhase = null;
  17. if (condition instanceof ConfigurationCondition) {
  18. requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
  19. }
  20. if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
  21. return true;
  22. }
  23. }
  24. return false;
  25. }
  26. }
  1. getConditionClasses:获取当前类所有@Conditional「直接方式、间接方式」其value属性设置的所有condition接口的子类。
  2. condition.matches:匹配condition接口每个子类的matches方法,只要存在一个false则表明当前类则不会添加到集合ConfigurationClasses中。更不会添加到IOC容器中。

2.解析@Component注解

如果当前类存在的组合注解中存在@Component注解都会触发。

memberClasses:获取当前类中的所有内部类。集合中所有内部类按照位置顺序从后往前依次添加到集合中。

  1. 排除掉非「配置类~Full模式&Lite模式」的候选类。
  2. 根据order接口进行排序「值越大优先级越低」。
  1. private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
  2. Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
  3. if (!memberClasses.isEmpty()) {
  4. List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
  5. for (SourceClass memberClass : memberClasses) {
  6. // 内部类成为候选类的条件
  7. if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
  8. !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
  9. candidates.add(memberClass);
  10. }
  11. }
  12. // 所有内部类排序,决定内部类触发的先后顺序
  13. OrderComparator.sort(candidates);
  14. for (SourceClass candidate : candidates) {
  15. ...
  16. processConfigurationClass(candidate.asConfigClass(configClass));
  17. }
  18. }
  19. }
  20. }
  1. public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
  2. if (metadata.isInterface()) {
  3. return false;
  4. }
  5. for (String indicator : candidateIndicators) {//4种类型注解:Import、ImportResuource、Component、ComponentScan
  6. if (metadata.isAnnotated(indicator)) {
  7. return true;
  8. }
  9. }
  10. return metadata.hasAnnotatedMethods(Bean.class.getName());
  11. }

 3.解析@ComponentScan注解

  1. componentScanParser.parse:扫描包路径「basePackage~启动类所在的包路径」下用户自定义的所有类。
  2. 遍历步骤1中的每个类。
  3. 最终递归方式回调ConfigurationClassParser#processConfigurationClass。
  1. public class ClassPathScanningCandidateComponentProvider{
  2. static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
  3. String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
  4. private final List<TypeFilter> excludeFilters = new ArrayList<>();
  5. private final List<TypeFilter> includeFilters = new ArrayList<>();
  6. private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
  7. Set<BeanDefinition> candidates = new LinkedHashSet<>();
  8. // 获取到类路径下全部的类
  9. String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
  10. resolveBasePackage(basePackage) + '/' + this.resourcePattern;
  11. Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
  12. for (Resource resource : resources) {
  13. MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
  14. // 遍历每一个类,判断是否满足候选类特性
  15. if (isCandidateComponent(metadataReader)) {
  16. ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
  17. sbd.setSource(resource);
  18. if (isCandidateComponent(sbd)) {
  19. candidates.add(sbd);
  20. }
  21. }
  22. }
  23. return candidates;
  24. }
  25. protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
  26. for (TypeFilter tf : this.excludeFilters) {
  27. if (tf.match(metadataReader, getMetadataReaderFactory())) {
  28. return false;
  29. }
  30. }
  31. for (TypeFilter tf : this.includeFilters) {
  32. if (tf.match(metadataReader, getMetadataReaderFactory())) {
  33. return isConditionMatch(metadataReader);
  34. }
  35. }
  36. return false;
  37. }
  38. }

默认情况下excludeFilters主要包含:AutoConfigurationExcludeFilter、TypeExcludeFilter。其中前者主要判断当前类是否存在注解@Configuration,后者应该是方便使用方自行扩展类的Filter。

默认情况下includeFilters主要包含:AnnotationTypeFilter,主要判断当前类是否存在注解@Component。

通常情况下候选类只要存在@Configuration、@Component任意一个注解都会被IOC容器自行管理的类。


4.解析@Import注解

通过@Import注解注入Bean的方式有如下三种:

  • 基于Configuration Class。例如:@Import({ NoOpMeterRegistryConfiguration.class}),导入的候选类只是普通的配置类。
  • 基于ImportSelector接口。
  • 基于ImportBeanDefinitionRegistrar接【AutoConfigurationPackages.Registrar.class】。特例参考AOP、事务相关功能。
  1. class ConfigurationClassParser {
  2. DeferredImportSelectorHandler deferredImportSelectorHandler = new DeferredImportSelectorHandler();
  3. private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
  4. Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
  5. for (SourceClass candidate : importCandidates) {// 当前类通过import注解导入的全部候选类
  6. // 候选类 是接口ImportSelector的子类
  7. if (candidate.isAssignable(ImportSelector.class)) {
  8. Class<?> candidateClass = candidate.loadClass();
  9. ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
  10. ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry);
  11. // 候选类 同时也是 接口DeferredImportSelector的子类 -- 启动类注解import导入的AutoConfigurationImportSelector
  12. if (selector instanceof DeferredImportSelector) {
  13. this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
  14. }else {
  15. // 直接调用候选类核心方法之selectImports,通过该方法再次得到需要加载到IOC容器的相关新的候选类
  16. String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
  17. Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
  18. // 递归调用分析新的候选类是否为ImportSelector类型 or 普通的配置类
  19. processImports(configClass, currentSourceClass, importSourceClasses, false);
  20. }
  21. // 候选类 是接口ImportBeanDefinitionRegistrar的子类
  22. }else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
  23. Class<?> candidateClass = candidate.loadClass();
  24. ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
  25. ParserStrategyUtils.invokeAwareMethods(egistrar, this.environment, this.resourceLoader, this.registry);
  26. // 候选类添加到 当前配置类configClass其importBeanDefinitionRegistrars属性中
  27. configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
  28. }else {
  29. // 候选类 是普通的配置类
  30. String className = candidate.getMetadata().getClassName();
  31. this.importStack.registerImport(currentSourceClass.getMetadata(), className);
  32. processConfigurationClass(candidate.asConfigClass(configClass));
  33. }
  34. }
  35. }
  36. }

DeferredImportSelectorHandler:延迟加载接口ImportSelector导入候选类的handler。通过spi机制加载spring.factories文件下的目标候选类。

延迟是提现在何处呢?

  • 如果当前配置类利用@Import导入的候选类是接口ImportSelector的子类但不是接口DeferredImportSelector的子类,则执行核心方法【selectImports----spi机制加载spring.factories文件下的目标候选类】时机如上文所示。
  • 如果当前配置类利用@Import导入的候选类只是接口ImportSelector的子类则马上通过selectImports方法再次获取该子类利用@Import导入的候选类。


 5.解析@Bean注解

  1. protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
  2. throws IOException {
  3. ...
  4. Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
  5. for (MethodMetadata methodMetadata : beanMethods) {
  6. configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
  7. }
  8. ...
  9. }

获取当前ConfigurationClass类通过@Bean注解注入的实例集合。


6.SourceClass

为啥多此一举需要把候选类封装为SourceClass?

因为在SpringBoot中候选类的加载分为两部分:JVM & ASM。

  1. 如果候选类已经通过反射方式获取到.class文件【启动类】,则直接将该候选类赋值为StandardAnnotationMetadata内部的内省类【候选类】。
  2. 如果候选类尚为编译后的.class文件,则通过类名判断是否为"java."包下的类:
  • 是,其实就是启动类加载器加载的类,直接通过反射获取。
  • 否,则通过ASM【SimpleMetadataReader】方式将候选类作为资源加载。
  1. private class SourceClass implements Ordered {
  2. public SourceClass(Object source) {
  3. this.source = source;
  4. if (source instanceof Class) {//内省类处理
  5. this.metadata = AnnotationMetadata.introspect((Class<?>) source);
  6. }
  7. else {
  8. this.metadata = ((MetadataReader) source).getAnnotationMetadata();
  9. }
  10. }
  11. public ConfigurationClass asConfigClass(ConfigurationClass importedBy) {
  12. if (this.source instanceof Class) {
  13. return new ConfigurationClass((Class<?>) this.source, importedBy);
  14. }
  15. return new ConfigurationClass((MetadataReader) this.source, importedBy);
  16. }
  17. ...
  18. }
importedBy:表示当前的sourceClass是被importedBy的@import引入的。

7.延迟候选类的BeanDefinitions

  1. class ConfigurationClassBeanDefinitionReader {
  2. public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
  3. TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
  4. // 集合configurationModel元素为全量bean的类信息
  5. for (ConfigurationClass configClass : configurationModel) {
  6. loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
  7. }
  8. }
  9. private void loadBeanDefinitionsForConfigurationClass(
  10. ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
  11. ...
  12. //当前配置类是通过注解@Import导入则注册配置类其BeanDefinition至bean 工厂中
  13. if (configClass.isImported()) {
  14. registerBeanDefinitionForImportedConfigurationClass(configClass);
  15. }
  16. // 将配置类中通过注解@Bean注入的bean,其BeanDefinition注册至bean 工厂中
  17. for (BeanMethod beanMethod : configClass.getBeanMethods()) {
  18. loadBeanDefinitionsForBeanMethod(beanMethod);
  19. }
  20. loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
  21. // 如果配置类是 接口ImportBeanDefinitionRegistrars 的子类则将该候选类的BeanDefinition注册至bean 工厂中
  22. loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
  23. }
  24. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/609390
推荐阅读
相关标签
  

闽ICP备14008679号