Spring源码分析之组件扫描配置类(上)

创建配置类解析器


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

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

根据不同的bean定义类型进行解析

public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

for (BeanDefinitionHolder holder : configCandidates) {
  BeanDefinition bd = holder.getBeanDefinition();
  try {
    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());
    }
  }
  catch (BeanDefinitionStoreException ex) {
    throw ex;
  }
  catch (Throwable ex) {
    throw new BeanDefinitionStoreException(
        "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
  }
}

processDeferredImportSelectors();
}

解析每一个配置类,判断是否需要跳过,是否已经解析过


protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
  if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
    return;
  }

  ConfigurationClass existingClass = this.configurationClasses.get(configClass);
  if (existingClass != null) {
    if (configClass.isImported()) {
      if (existingClass.isImported()) {
        existingClass.mergeImportedBy(configClass);
      }
      // Otherwise ignore new imported config class; existing non-imported class overrides it.
      return;
    }
    else {
      // Explicit bean definition found, probably replacing an import.
      // Let's remove the old one and go with the new one.
      this.configurationClasses.remove(configClass);
      for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext(); ) {
        if (configClass.equals(it.next())) {
          it.remove();
        }
      }
    }
  }

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

  this.configurationClasses.put(configClass, configClass);
}

首先递归处理任何成员嵌套类

// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass);
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
  for (SourceClass memberClass : sourceClass.getMemberClasses()) {
    if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
        !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
      if (this.importStack.contains(configClass)) {
        this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
      }
      else {
        this.importStack.push(configClass);
        try {
          processConfigurationClass(memberClass.asConfigClass(configClass));
        }
        finally {
          this.importStack.pop();
        }
      }
    }
  }
}

解析注解PropertySources,PropertySource


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

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
  String name = propertySource.getString("name");
  if (!StringUtils.hasLength(name)) {
    name = null;
  }
  String encoding = propertySource.getString("encoding");
  if (!StringUtils.hasLength(encoding)) {
    encoding = null;
  }
  String[] locations = propertySource.getStringArray("value");
  Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
  boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

  Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
  PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
      DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

  for (String location : locations) {
    try {
      String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
      Resource resource = this.resourceLoader.getResource(resolvedLocation);
      addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
    }
    catch (IllegalArgumentException ex) {
      // from resolveRequiredPlaceholders
      if (!ignoreResourceNotFound) {
        throw ex;
      }
    }
    catch (FileNotFoundException ex) {
      // from ResourcePropertySource constructor
      if (!ignoreResourceNotFound) {
        throw ex;
      }
    }
  }
}

解析注解ComponentScan,ComponentScans,使用解析器ComponentScanAnnotationParser扫描指定路径下的bean定义

// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
    sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
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
    Set<BeanDefinitionHolder> scannedBeanDefinitions =
        this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
    // Check the set of scanned definitions for any further config classes and parse recursively if necessary
    for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
      if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
        parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
      }
    }
  }
}

获取带有注解Import类型信息


// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), true);

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
  Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
  Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
  collectImports(sourceClass, imports, visited);
  return imports;
}

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) throws IOException {
  if (visited.add(sourceClass)) {
    for (SourceClass annotation : sourceClass.getAnnotations()) {
      String annName = annotation.getMetadata().getClassName();
      if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
        collectImports(annotation, imports, visited);
      }
    }
    imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
  }
}

解析注解Import


private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
    Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {

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

  if (checkForCircularImports && isChainedImportOnStack(configClass)) {
    this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
  }
  else {
    this.importStack.push(configClass);
    try {
      for (SourceClass candidate : importCandidates) {
        if (candidate.isAssignable(ImportSelector.class)) {
          // Candidate class is an ImportSelector -> delegate to it to determine imports
          Class<?> candidateClass = candidate.loadClass();
          ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
          ParserStrategyUtils.invokeAwareMethods(
              selector, this.environment, this.resourceLoader, this.registry);
          if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
            this.deferredImportSelectors.add(
                new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
          }
          else {
            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
            Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
            processImports(configClass, currentSourceClass, importSourceClasses, false);
          }
        }
        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 =
              BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
          ParserStrategyUtils.invokeAwareMethods(
              registrar, this.environment, this.resourceLoader, this.registry);
          configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
        }
        else {
          // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
          // process it as an @Configuration class
          this.importStack.registerImport(
              currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
          processConfigurationClass(candidate.asConfigClass(configClass));
        }
      }
    }
    catch (BeanDefinitionStoreException ex) {
      throw ex;
    }
    catch (Throwable ex) {
      throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
          configClass.getMetadata().getClassName() + "]", ex);
    }
    finally {
      this.importStack.pop();
    }
  }
}

解析注解ImportResource


// Process any @ImportResource annotations
if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
  AnnotationAttributes importResource =
      AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
  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);
  }
}

解析带有Bean注解的方法

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

解析接口上带有注解Bean的方法


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

private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
  for (SourceClass ifc : sourceClass.getInterfaces()) {
    Set<MethodMetadata> beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName());
    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);
  }
}

解析父类,如果没有父类的就返回空结束该类型的解析


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

// No superclass -> processing is complete
return null;

最后处理延迟导入的资源


private void processDeferredImportSelectors() {
  List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
  this.deferredImportSelectors = null;
  Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);

  for (DeferredImportSelectorHolder deferredImport : deferredImports) {
    ConfigurationClass configClass = deferredImport.getConfigurationClass();
    try {
      String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
      processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
    }
    catch (BeanDefinitionStoreException ex) {
      throw ex;
    }
    catch (Throwable ex) {
      throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
          configClass.getMetadata().getClassName() + "]", ex);
    }
  }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring 源码分析是一个相对复杂和庞大的话题,无法在一次对话中详细讨论。不过,我可以给你提供一些关于 Spring 源码分析的一般指导和建议。 1. 了解 Spring 的核心模块:Spring 框架是模块化的,每个模块都有不同的功能和职责。在开始源码分析之前,你需要先了解 Spring 的核心模块,如 Spring Core、Spring MVC、Spring Data 等。 2. 阅读官方文档和源码注释:Spring 框架的官方文档和源码注释是你学习和理解源码的重要资源。官方文档提供了对 Spring 各个模块的详细说明,源码注释则解释了代码的作用和实现细节。 3. 调试和跟踪代码:在进行源码分析时,调试器是你的好帮手。通过设置断点、单步跟踪以及观察变量的值,你可以深入理解代码的执行流程和逻辑。 4. 理解设计模式和原理:Spring 框架采用了许多设计模式和原理来实现其功能。在分析源码时,你需要熟悉这些设计模式和原理,例如依赖注入、AOP、工厂模式等。 5. 参考开源社区和博客:Spring 框架是一个非常活跃的开源社区,许多开发者在博客和论坛上分享了他们的源码分析和理解。阅读这些文章可以帮助你更好地理解 Spring 框架的实现细节。 请记住,深入分析 Spring 源码需要耐心和时间投入,同时也需要有一定的 Java 和设计模式的基础。希望这些指导对你有所帮助!如果你有具体的问题或者需要更详细的信息,欢迎继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值