Spring配置类解析(中)

梦想不抛弃苦心追求的人,只要不停止追求,你们会沐浴在梦想的光辉之中。再美好的梦想与目标,再完美的计划和方案,如果不能尽快在行动中落实,最终只能是纸上谈兵,空想一番。只要瞄准了大方向,坚持不懈地做下去,才能够扫除挡在梦想前面的障碍,实现美好的人生蓝图。Spring配置类解析(中),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

一、解析配置类核心流程

获取到所有的配置类之后,对它们进行排序,按照优先级对它们进行解析

// Sort by previously determined @Order value, if applicable
// 通过@Order可以排序,升序排序,order越小越靠前
configCandidates.sort((bd1, bd2) -> {
    int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
    int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
    return Integer.compare(i1, i2);
});

下面生成配置类解析对象,进行解析

// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
    this.metadataReaderFactory, this.problemReporter, this.environment,
    this.resourceLoader, this.componentScanBeanNameGenerator, registry);

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());

do {
    StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");

    // 解析配置类,会把每个BeanDefinitionHolder首先封装为ConfigurationClass
    // 在这个过程中会进行扫描、导入等步骤,从而会找到其他的ConfigurationClass
    // 解析配置类的结果是什么?
    parser.parse(candidates);  // AppConfig.class--->BeanDefinition
    ……
}
while (!candidates.isEmpty());

1.1 循环解析

遍历所有配置类,依次进行解析

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        // 解析BeanDefinition所对应的类
        if (bd instanceof AnnotatedBeanDefinition) {
            parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
        }
        else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
            parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
        }
        else {
            parse(bd.getBeanClassName(), holder.getBeanName());
        }
    }

    // 处理deferredImportSelectors,表示当前所有配置类解析完了之后才执行
    // deferredImportSelector表示推迟的ImportSelector,正常的ImportSelector是在解析配置类的过程中执行的
    this.deferredImportSelectorHandler.process();
}

对元数据进行包装

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
   // 把类的元信息和beanName封装为ConfigurationClass
   processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}

如果类上有@Conditional注解,只有条件满足时才会进行解析

当前配置类可能是某个类的子类,所以解析完当前类后,再去解析其父类,知道没有父类为止

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {

   // 条件注解,就是看有没有类上是否有@Conditional注解,如果有,则进行条件匹配
   if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
      return;
   }
   ……

   // Recursively process the configuration class and its superclass hierarchy.
   SourceClass sourceClass = asSourceClass(configClass, filter);
   do {
      sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
   }
   while (sourceClass != null);

   // ConfigurationClass重写了equals方法,只要两个ConfigurationClass对应的className相等就可以
   this.configurationClasses.put(configClass, configClass);
}

1.2 处理@Component注解内部类

如果类上有@Component注解,则去判断是否有内部类为配置类

if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
   // Recursively process any member (nested) classes first
   // 处理内部类
   // 在解析一个配置类时,如果类上有@Component,则会判断内部类是不是lite配置类并进行解析,并且会记录为被导入的
   processMemberClasses(configClass, sourceClass, filter);
}

如果有内部类,与前面判断配置类一样的逻辑,进行判断,然后排序,接着遍历进行递归解析,如果出现了循环引入的情况,直接抛异常

private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
      Predicate<String> filter) throws IOException {

   Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
   if (!memberClasses.isEmpty()) {
      List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
      for (SourceClass memberClass : memberClasses) {
         // 内部类是不是lite配置类
         if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
               !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
            candidates.add(memberClass);
         }
      }
      // 排序
      OrderComparator.sort(candidates);

      for (SourceClass candidate : candidates) {
         // AppConfig中有一个内部类A, A上用@Import导入AppConfig.class,就出现了循环import
         if (this.importStack.contains(configClass)) {
            // 就是直接抛异常
            this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
         }
         else {
            this.importStack.push(configClass);
            try {
               processConfigurationClass(candidate.asConfigClass(configClass), filter);
            }
            finally {
               this.importStack.pop();
            }
         }
      }
   }
}

1.3 处理@PropertySources注解配置类

解析@PropertySources指定的配置文件,然后将属性添加到environment

for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
      sourceClass.getMetadata(), PropertySources.class,
      org.springframework.context.annotation.PropertySource.class)) {
   if (this.environment instanceof ConfigurableEnvironment) {
      processPropertySource(propertySource);
   }
   else {
      logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
            "]. Reason: Environment must implement ConfigurableEnvironment");
   }
}

1.4 处理@ComponentScan注解配置类

判断当前配置类上面有@ComponentScan或@ComponentScans注解

// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
    sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);

如果有这两种注解,则获取注解上的参数,然后根据value指定的包路径,调用componentScanParser的parse()去进行扫描,里面具体的调用就是《BeanDefinition创建过程》中介绍的doScan()方法

if (!componentScans.isEmpty() &&
      !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
   for (AnnotationAttributes componentScan : componentScans) {
      // The config class is annotated with @ComponentScan -> perform the scan immediately
      // 这里就会进行扫描,得到的BeanDefinition会注册到Spring容器中
      Set<BeanDefinitionHolder> scannedBeanDefinitions =
            this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
    	……
      }
   }
}

扫描会生成新的BeanDefiniton,然后再去判断这些新生成的BeanDefinition中是否有配置类,如果有递归调用解析方法进行解析

// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
    BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
    if (bdCand == null) {
        bdCand = holder.getBeanDefinition();
    }
    // 检查扫描出来的BeanDefinition是不是配置类(full和lite)
    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
        parse(bdCand.getBeanClassName(), holder.getBeanName());
    }

1.5 处理@Import注解配置类

根据getImports(sourceClass)去获取当前配置类上Import注解引入的类,以及配置类上其他注解通过Import导入的类,然后调用processImports()去解析@Import注解

// Process any @Import annotations
// getImports(sourceClass)会拿到@Import导入的类
// 如果导入的是普通类,那么会直接把它当做配置类来解析
// 如果导入的是普通ImportSelector,那么会将返回的类再次调用processImports()
// 如果导入的是特殊ImportSelector,DeferredImportSelector,那么暂时不会处理,会在解析完所有当前这轮配置类后进行导入,将返回的类再次调用processImports()
// 如果导入的是ImportBeanDefinitionRegistrar,那么暂时不会处理,会在解析完所有当前这轮配置类后,将配置类解析成为BeanDefinition之后进行调用
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

如果没有@Import注解指定的类,直接返回

如果出现了循环引入,则直接报错

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
      boolean checkForCircularImports) {

   if (importCandidates.isEmpty()) {
      return;
   }

   if (checkForCircularImports && isChainedImportOnStack(configClass)) {
      this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
   }
    ……
}

遍历Import导入进来的类,如果import的类实现了ImportSelector接口,则将该类进行实例化,然后进一步判断该类是否是DeferredImportSelector类型,如果是该类型,则加入到deferredImportSelectorHandler,表示推迟执行,会在当前这一轮配置类扫描后执行,而不是所有配置都扫描完执行

如果是一个普通的ImportSelector,则调用它的selectImports()方法得到导入的类,递归调用processImports()方法进行解析

for (SourceClass candidate : importCandidates) {
    // 如果import的类实现了ImportSelector接口
    if (candidate.isAssignable(ImportSelector.class)) {
        // Candidate class is an ImportSelector -> delegate to it to determine imports
        //加载类,创建实例
        Class<?> candidateClass = candidate.loadClass();
        ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                                                                       this.environment, this.resourceLoader, this.registry);
        Predicate<String> selectorFilter = selector.getExclusionFilter();
        if (selectorFilter != null) {
            exclusionFilter = exclusionFilter.or(selectorFilter);
        }
        // 如果import的是DeferredImportSelector,表示推迟导入
        //
        if (selector instanceof DeferredImportSelector) {
            this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
        } else {
            // 如果import的是普通的ImportSelector
            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
            // 继续处理selectImports()所返回的类
            Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
            processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
        }
    }
    ……
}

如果导入的类实现了ImportBeanDefinitionRegistrar接口,则将其实例化之后,通过addImportBeanDefinitionRegistrar缓存在当前配置类的importBeanDefinitionRegistrars属性中

如果Import的就是一个普通的类,不满足上述的两大类条件,则会把这个普通的类直接当作配置类去进行配置类解析

// 如果import的类实现了ImportBeanDefinitionRegistrar接口
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
    // Candidate class is an ImportBeanDefinitionRegistrar ->
    // delegate to it to register additional bean definitions
    Class<?> candidateClass = candidate.loadClass();
    ImportBeanDefinitionRegistrar registrar =
        ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                                             this.environment, this.resourceLoader, this.registry);
    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
// 如果import的类就是普通的类
else {
    // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
    // process it as an @Configuration class
    this.importStack.registerImport(
        currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
    // 注意,在asConfigClass方法中,不仅会将candidate生成一个ConfigurationClass,还会记录一下candidate是被哪个类导入的importedBy
    processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}

在配置类的最上层解析方法中,当遍历解析完这一轮所有的配置类之后,才会去处理@Import注解导入的DeferredImportSelector类型的类

public void parse(Set<BeanDefinitionHolder> configCandidates) {
   for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      //解析配置类
       ……
   }

   // 处理deferredImportSelectors,表示当前所有配置类解析完了之后才执行
   // deferredImportSelector表示推迟的ImportSelector,正常的ImportSelector是在解析配置类的过程中执行的
   this.deferredImportSelectorHandler.process();
}

1.6 处理@ImportResource注解

@ImportResource可以用于导入spring的xml文件,然后解析xml文件生成定义的bean

如果配置类有@ImportResource注解,则获取指定的值,@ImportResource支持占位符填充,所以拿到注解指定的值之后,调用resolveRequiredPlaceholders()进行占位符解析,获取最终的资源名称,但此时并不会去直接解析xml文件,而是将其将其加入到当前配置类的importedResources中,后面再处理

// Process any @ImportResource annotations
AnnotationAttributes importResource =
      AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
   String[] resources = importResource.getStringArray("locations");
   Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
   for (String resource : resources) {
      String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
      configClass.addImportedResource(resolvedResource, readerClass);
   }
}

1.7 处理@Bean注解方法

解析配置类中的@Bean方法,只是把这些方法找出来,然后封装成BeanMethon缓存在当前配置类的beanMethods属性总,并没有真正处理@Bean

// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
   configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

通过ASM技术获取所有@Bean方法

private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
    AnnotationMetadata original = sourceClass.getMetadata();
    Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
    if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
        // Try reading the class file via ASM for deterministic declaration order...
        // Unfortunately, the JVM's standard reflection returns methods in arbitrary
        // order, even between different runs of the same application on the same JVM.
        // 上面这段注释的意思是,JVM反射所返回的方法顺序是任意的,直接取beanMethods做过测试,多运行几次确实是返回的方法顺序是不固定的
        AnnotationMetadata asm =
            this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
        Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
        if (asmMethods.size() >= beanMethods.size()) {
            Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
            for (MethodMetadata asmMethod : asmMethods) {
                for (MethodMetadata beanMethod : beanMethods) {
                    if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
                        selectedMethods.add(beanMethod);
                        break;
                    }
                }
            }
            if (selectedMethods.size() == beanMethods.size()) {
                // All reflection-detected methods found in ASM method set -> proceed
                beanMethods = selectedMethods;
            }
        }
    }
    return beanMethods;
}

1.8 处理配置类所实现接口中的@Bean方法

解析配置类所实现的接口中的@Bean,但并没有真正处理@Bean,只是暂时找出来

// Process default methods on interfaces
processInterfaces(configClass, sourceClass);

递归遍历所有实现的接口,判断是否有@Bean方法,然后加入的当前配置类的BeanMethod属性中

/**
 * Register default methods on interfaces implemented by the configuration class.
 */
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
   for (SourceClass ifc : sourceClass.getInterfaces()) {
      Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
      for (MethodMetadata methodMetadata : beanMethods) {
         if (!methodMetadata.isAbstract()) {
            // A default method or other concrete method on a Java 8+ interface...
            configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
         }
      }
      processInterfaces(configClass, ifc);
   }
}

1.9 返回配置类父类,循环解析

如果当前配置类有父类,则返回其父类,然后对父类进行解析,执行上面的流程

// Process superclass, if any
if (sourceClass.getMetadata().hasSuperClass()) {
   String superclass = sourceClass.getMetadata().getSuperClassName();
   if (superclass != null && !superclass.startsWith("java") &&
         !this.knownSuperclasses.containsKey(superclass)) {
      this.knownSuperclasses.put(superclass, configClass);
      // Superclass found, return its annotation metadata and recurse
      return sourceClass.getSuperClass();
   }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/153655.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!