你知道Spring是怎么解析配置类的吗?(3)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 你知道Spring是怎么解析配置类的吗?(3)

2、解析源码分析


在上面的源码分析中,我们已经能够确定了Spring是通过ConfigurationClassParser的parse方法来完成对配置类的解析的。Spring对类的取名可以说是很讲究了,ConfigurationClassParser直译过来就是配置类解析器。接着我们就来看看它的源码


2.1、parse方法

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    this.deferredImportSelectors = new LinkedList<>();
    // 遍历所有的配置类,一个个完成解析
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            // 三个判断最终都会进入到同一个方法---->processConfigurationClass方法
            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);
        }
    }
    // 对ImportSelector进行延迟处理
    processDeferredImportSelectors();
}

2.2、processConfigurationClass方法

  protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    // 解析@Conditional注解,判断是否需要解析
    if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
      return;
    }
    // 判断解析器是否已经解析过这个配置类了
    ConfigurationClass existingClass = this.configurationClasses.get(configClass);
    // 不为null,说明已经解析过了
    if (existingClass != null) {
      // 如果这个要被解析的配置类是被@Import注解导入的
      if (configClass.isImported()) {
        // 并且解析过的配置类也是被导入的
        if (existingClass.isImported()) {
          // 那么这个配置类的导入类集合中新增当前的配置类的导入类,(A通过@Import导入了B,那么A就是B的导入类,B被A导入)
          existingClass.mergeImportedBy(configClass);
        }
        // Otherwise ignore new imported config class; existing non-imported class overrides it.
        // 如果已经解析过的配置类不是被导入的,那么直接忽略新增的这个被导入的配置类。也就是说如果一个配置类同时被@Import导入以及正常的
        // 添加到容器中,那么正常添加到容器中的配置类会覆盖被导入的类
        return;
      }
      else {
        // Explicit bean definition found, probably replacing an import.
        // Let's remove the old one and go with the new one.
        // 就是说新要被解析的这个配置类不是被导入的,所以这种情况下,直接移除调原有的解析的配置类
        // 为什么不是remove(existingClass)呢?可以看看hashCode跟equals方法
        // remove(existingClass)跟remove(configClass)是等价的
        this.configurationClasses.remove(configClass);
        this.knownSuperclasses.values().removeIf(configClass::equals);
      }
    }
    // Recursively process the configuration class and its superclass hierarchy.
    // 下面这段代码主要是递归的处理配置类及其父类
    //  将配置类封装成一个SourceClass方便进行统一的处理
    SourceClass sourceClass = asSourceClass(configClass);
    do {
      // doxxx方法,真正干活的方法,对配置类进行处理,返回值是当前这个类的父类
      sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    }
    while (sourceClass != null);
    this.configurationClasses.put(configClass, configClass);
  }

2.3、doProcessConfigurationClass方法

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
    throws IOException {
    // Recursively process any member (nested) classes first
    // 递归处理内部类
    processMemberClasses(configClass, sourceClass);
    // Process any @PropertySource annotations
    // 处理@PropertySources跟@PropertySource注解,将对应的属性资源添加容器中(实际上添加到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.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                        "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }
    // Process any @ComponentScan annotations、
    // 处理@ComponentScan,@ComponentScans注解,真正进行扫描的地方就是这里
    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 needed
        // 检查扫描出来的bd是否是配置类,如果是配置类递归进行解析
        for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
          // 一般情况下getOriginatingBeanDefinition获取到的都是null
          // 什么时候不为null呢?,参考:ScopedProxyUtils.createScopedProxy方法
          // 在创建一个代理的bd时不会为null
          BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
          if (bdCand == null) {
            bdCand = holder.getBeanDefinition();
          }
          // 判断扫描出来的bd是否是一个配置类,如果是的话继续递归处理
          if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
            parse(bdCand.getBeanClassName(), holder.getBeanName());
          }
        }
      }
    }
    // Process any @Import annotations
    // 处理@Import注解
    processImports(configClass, sourceClass, getImports(sourceClass), true);
    // Process any @ImportResource annotations
    // 处理@ImportResource注解
    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);
        }
    }
    // Process individual @Bean methods
    // 处理@Bean注解
    // 获取到被@Bean标注的方法
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        // 添加到configClass中
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }
    // Process default methods on interfaces
    // 处理接口中的default方法
    processInterfaces(configClass, sourceClass);
    // 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();
        }
    }
    // No superclass -> processing is complete
    return null;
}

可以看到,在doProcessConfigurationClass真正完成了对配置类的解析,一共做了下面几件事


  1. 解析配置类中的内部类,看内部类中是否有配置类,如果有进行递归处理
  2. 处理配置类上的@PropertySources跟@PropertySource注解
  3. 处理@ComponentScan,@ComponentScans注解
  4. 处理@Import注解
  5. 处理@ImportResource注解
  6. 处理@Bean注解
  7. 处理接口中的default方法
  8. 返回父类,让外部的循环继续处理当前配置类的父类

我们逐一进行分析


2.4、处理配置类中的内部类


这段代码非常简单,限于篇幅原因我这里就不再专门分析了,就是获取到当前配置类中的所有内部类,然后遍历所有的内部类,判断是否是一个配置类,如果是配置类的话就递归进行解析


2.5、处理@PropertySource注解


代码也非常简单,根据注解中的信息加载对应的属性文件然后添加到容器中


2.6、处理@ComponentScan注解


这个段我们就需要看一看了,Spring在这里完成的扫描,我们直接查看其核心方法,org.springframework.context.annotation.ComponentScanAnnotationParser#parse

  public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    // 第一步就创建了一个ClassPathBeanDefinitionScanner对象
    // 在这里我们就知道了,Spring在进行扫描时没有使用在最开始的时候创建的那个对象进行扫描
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
        componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    // 解析成bd时采用的beanName的生成规则
    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
    scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
        BeanUtils.instantiateClass(generatorClass));
    // 配置这个扫描规则下的ScopedProxyMode的默认值
    ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
    if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
      scanner.setScopedProxyMode(scopedProxyMode);
    }
    else {
      Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
      scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
    }
    // 配置扫描器的匹配规则
    scanner.setResourcePattern(componentScan.getString("resourcePattern"));
    // 配置扫描器需要扫描的组件
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
      for (TypeFilter typeFilter : typeFiltersFor(filter)) {
        scanner.addIncludeFilter(typeFilter);
      }
    }
    // 配置扫描器不需要扫描的组件
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
      for (TypeFilter typeFilter : typeFiltersFor(filter)) {
        scanner.addExcludeFilter(typeFilter);
      }
    }
    // 配置默认是否进行懒加载
    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {
      scanner.getBeanDefinitionDefaults().setLazyInit(true);
    }
    // 配置扫描器扫描的包名
    Set<String> basePackages = new LinkedHashSet<>();
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
      String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
          ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      Collections.addAll(basePackages, tokenized);
    }
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    if (basePackages.isEmpty()) {
      basePackages.add(ClassUtils.getPackageName(declaringClass));
    }
    // 排除自身
    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
      @Override
      protected boolean matchClassName(String className) {
        return declaringClass.equals(className);
      }
    });
    // 在完成对扫描器的配置后,直接调用其doScan方法进行扫描
    return scanner.doScan(StringUtils.toStringArray(basePackages));
  }

看到了吧,第一步就创建了一个ClassPathBeanDefinitionScanner,紧接着通过解析注解,对这个扫描器进行了各种配置,然后调用doScan方法完成了扫描。


2.7、处理@Import注解

  private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
    // 没有要导入的类,直接返回
    if (importCandidates.isEmpty()) {
      return;
    }
    // checkForCircularImports:Spring中写死的为true,需要检查循环导入
    // isChainedImportOnStack方法:检查导入栈中是否存在了这个configClass,如果存在了说明
    // 出现了A import B,B import A的情况,直接抛出异常
    if (checkForCircularImports && isChainedImportOnStack(configClass)) {
      this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
    }
    else {
      // 没有出现循环导入,先将当前的这个配置类加入到导入栈中
      this.importStack.push(configClass);
      try {
        // 遍历所有要导入的类
        for (SourceClass candidate : importCandidates) {
          // 如果要导入的类是一个ImportSelector
          if (candidate.isAssignable(ImportSelector.class)) {
            // Candidate class is an ImportSelector -> delegate to it to determine imports
            // 反射创建这个ImportSelector
            Class<?> candidateClass = candidate.loadClass();
            ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
            // 执行xxxAware方法
            ParserStrategyUtils.invokeAwareMethods(
                selector, this.environment, this.resourceLoader, this.registry);
            // 如果是一个DeferredImportSelector,添加到deferredImportSelectors集合中去
            // 在所有的配置类完成解析后再去处理deferredImportSelectors集合中的ImportSelector
            if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
              this.deferredImportSelectors.add(
                  new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
            }
            else {
              // 不是一个DeferredImportSelector,那么通过这个ImportSelector获取到要导入的类名
              String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
              // 将其转换成SourceClass
              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
            // 如果是一个ImportBeanDefinitionRegistrar
            // 先通过反射创建这个ImportBeanDefinitionRegistrar
            Class<?> candidateClass = candidate.loadClass();
            ImportBeanDefinitionRegistrar registrar =
                BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
            // 再执行xxxAware方法
            ParserStrategyUtils.invokeAwareMethods(
                registrar, this.environment, this.resourceLoader, this.registry);
            // 最后将其添加到configClass的importBeanDefinitionRegistrars集合中
            // 之后会统一调用其ImportBeanDefinitionRegistrar的registerBeanDefinitions方法,将对应的bd注册到容器中
            configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
          }
          else {
            // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
            // process it as an @Configuration class
            // 既不是一个ImportSelector也不是一个ImportBeanDefinitionRegistrar,直接导入一个普通类
            // 并将这个类作为配置类进行递归处理
            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();
      }
    }
  }

2.8、处理@ImportResource注解


代码也很简单,在指定的位置加载资源,然后添加到configClass中。一般情况下,我们通过@ImportResource注解导入的就是一个XML配置文件。将这个Resource添加到configClass后,Spring会在后文中解析这个XML配置文件然后将其中的bd注册到容器中,可以参考org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions方法


2.9、处理@Bean注解


将配置类中所有的被@Bean标注的方法添加到configClass的BeanMethod集合中


2.10、处理接口中的default方法


代码也很简单,Java8中接口能定义default方法,这里就是处理接口中的default方法,看其是否有@Bean标注的方法


到此为止,我们分析完了整个解析的过程。可以发现Spring将所有解析到的配置信息都存储在了ConfigurationClass类中,但是到目前为止这些存储的信息都没有进行使用。那么Spring是在哪里使用的这些信息呢?回到我们的第三段代码,其中有一行代码如图所示:

微信图片_20221113153009.png

也就是在这里Spring完成了对解析好的配置类的信息处理。


2.11、加载解析完成的配置信息

// configurationModel:被解析完成了配置类集合,其中保存了@Bean注解解析信息,@Import注解解析信息等等
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
        // 调用这个方法完成的加载
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}
private void loadBeanDefinitionsForConfigurationClass(
    ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
  // 判断是否需要跳过,例如A导入了B,A不满足加载的条件需要被跳过,那么B也应该被跳过
    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        String beanName = configClass.getBeanName();
        if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
            this.registry.removeBeanDefinition(beanName);
        }
        this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        return;
    }
    // 判断配置类是否是被导入进来的,实际的代码就是判断解析出来的configclass中的importedBy集合是否为空
    // 那么这个importedBy集合是做什么的呢?
    // 例如A通过@Import导入了B,那么解析B得到得configclass中得importedBy集合就包含了A
    // 简而言之,importedBy集合就是导入了这个类的其它类(可能同时被多个类导入)
    // 在前文中我们也分析过了,被多个类同时导入时会调用mergeImportedBy方法在集合中添加一个元素
    if (configClass.isImported()) {
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    // 解析@Bean标注的Method得到对应的BeanDefinition并注册到容器中
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }
  // 解析导入的配置文件,并将从中得到的bd注册到容器中
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    // 执行configClass中的所有ImportBeanDefinitionRegistrar的registerBeanDefinitions方法
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

这段代码阅读起来还是非常简单的,这里我就跟大家一起看下BeanMethod的相关代码,主要是为了让大家对BeanDefinition的理解能够更加深入,其源码如下

  private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    ConfigurationClass configClass = beanMethod.getConfigurationClass();
    MethodMetadata metadata = beanMethod.getMetadata();
    String methodName = metadata.getMethodName();
    // 根据@Conditional注解判断是否需要跳过
    if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
      configClass.skippedBeanMethods.add(methodName);
      return;
    }
    if (configClass.skippedBeanMethods.contains(methodName)) {
      return;
    }
        // 获取@Bean注解中的属性
    AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
    Assert.state(bean != null, "No @Bean annotation attributes");
    // 从这里可以看出,如果没有配置beanName,默认会取方法名称作为beanName
    List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
    String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
    // 注册别名
    for (String alias : names) {
      this.registry.registerAlias(beanName, alias);
    }
    // isOverriddenByExistingDefinition这个方法判断的是当前注册的bd是否被原有的存在的bd所覆盖了
        // 什么是覆盖呢?后文中我们详细分析
    if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
            // 满足下面这个if的话意味着@Bean创建的bean跟@Bean标注的方法所所在的配置类的名称一样了,这种情况下直接抛出异常
      if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
        throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
            beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
            "' clashes with bean name for containing configuration class; please make those names unique!");
      }
      return;
    }
    // 创建一个ConfigurationClassBeanDefinition,从这里可以看出通过@Bean创建的Bean所对应的bd全是ConfigurationClassBeanDefinition
    ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
    beanDef.setResource(configClass.getResource());
    beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
        // @Bean是静态的,那么只需要知道静态方法所在类名以及方法名就能执行这个方法了
    if (metadata.isStatic()) {
      // static @Bean method
      beanDef.setBeanClassName(configClass.getMetadata().getClassName());
      beanDef.setFactoryMethodName(methodName);
    }
    else {
            // 
      // instance @Bean method
      beanDef.setFactoryBeanName(configClass.getBeanName());
      beanDef.setUniqueFactoryMethodName(methodName);
    }
        // 接下来的代码就是设置一些bd的属性,然后将bd注册到容器中,相关的源码在之前的文章中已经分析过了
        // 这里我就不在分析了,参考本文推荐阅读文章的《读源码,我们可以从第一行读起》
    //.....
  }

上面这个方法的主要目的就是将@Bean标注的方法解析成BeandDefinition然后注册到容器中。关于这个方法我们可以对比下之前分析过的org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean方法。对比我们可以发现,这两个方法最大的不同在于一个是基于Class对象的,而另一个则是基于Method对象的。


正因为如此,所有它们有一个很大的不同点在于BeanDefinition中BeanClasss属性的设置。可以看到,对于@Bean形式创建的Bean其BeanDefinition中是没有设置BeanClasss属性的,但是额外设置了其它的属性


  • 静态方法下,设置了BeanClassName以及FactoryMethodName属性,其中的BeanClassName是静态方法所在类的类名,FactoryMethodName是静态方法的方法名
  • 实例方法下,设置了FactoryBeanName以及FactoryMethodName属性,其中FactoryBeanName是实例对应的Bean的名称,而FactoryMethodName是实例中对应的方法名

之所以不用设置BeanClasss属性是因为,通过指定的静态方法或者指定的实例中的方法也能唯一确定一个Bean。


除此之外,注册@Bean形式得到的BeanDefinition时,还进行了一个isOverriddenByExistingDefinition(beanMethod, beanName)方法的判断,这个方法的主要作用是判断当前要注册的bean是否被之前已经存在的Bean覆盖了。但是在直接通过AnnotatedBeanDefinitionReader#doRegisterBean方法注册Bean时是没有进行这个判断的,如果存在就直接覆盖了,而不会用之前的bd来覆盖现在要注册的bd。这是为什么呢?据笔者自己的理解,是因为Spring将Bean也是分成了三六九等的,通过@Bean方式得到的bd可以覆盖扫描出来的普通bd(ScannedGenericBeanDefinition),但是不能覆盖配置类,所以当已经存在的bd是一个ScannedGenericBeanDefinition时,那么直接进行覆盖,但是当已经存在的bd是一个配置类时,就不能进行覆盖了,要使用已经存在的bd来覆盖本次要注册的bd。


到此为止,我们就完成了Spring中的整个配置类解析、注册的相关源码分析,不过还没完,我们还得解决一个问题,就是为什么要在配置类上添加@Configuration注解,在之前的源码分析中我们知道,添加@Configuration注解的作用就是讲配置类标志成了一个full configurationClass,这个的目的是什么呢?本来是打算一篇文章写完的,不过实在是太长了,接近6w字,所以还是拆成了两篇,预知后事如何,请看下文:

配置类为什么要添加@Configuration注解呢?


总结


我们结合上篇文章彻底读懂Spring(一)读源码,我们可以从第一行读起整理下目前Spring的执行流程


微信图片_20221113153234.png

原图地址: 原图

清晰的知道了执行的流程,我们再来回想下postProcessBeanDefinitionRegistry做了什么。

微信图片_20221113153312.png

相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
14 2
|
4天前
|
Java 开发者 微服务
手写模拟Spring Boot自动配置功能
【11月更文挑战第19天】随着微服务架构的兴起,Spring Boot作为一种快速开发框架,因其简化了Spring应用的初始搭建和开发过程,受到了广大开发者的青睐。自动配置作为Spring Boot的核心特性之一,大大减少了手动配置的工作量,提高了开发效率。
18 0
|
28天前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
35 0
|
21天前
|
Java API Spring
在 Spring 配置文件中配置 Filter 的步骤
【10月更文挑战第21天】在 Spring 配置文件中配置 Filter 是实现请求过滤的重要手段。通过合理的配置,可以灵活地对请求进行处理,满足各种应用需求。还可以根据具体的项目要求和实际情况,进一步深入研究和优化 Filter 的配置,以提高应用的性能和安全性。
|
21天前
|
搜索推荐 Java Spring
Spring Filter深度解析
【10月更文挑战第21天】Spring Filter 是 Spring 框架中非常重要的一部分,它为请求处理提供了灵活的控制和扩展机制。通过合理配置和使用 Filter,可以实现各种个性化的功能,提升应用的安全性、可靠性和性能。还可以结合具体的代码示例和实际应用案例,进一步深入探讨 Spring Filter 的具体应用和优化技巧,使对它的理解更加全面和深入。
|
13天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
26 1
|
20天前
|
域名解析 存储 缓存
DNS是什么?内网电脑需要配置吗?
【10月更文挑战第22天】DNS是什么?内网电脑需要配置吗?
75 1
|
23天前
|
存储 Java API
详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
【10月更文挑战第19天】深入剖析Java Map:不仅是高效存储键值对的数据结构,更是展现设计艺术的典范。本文从基本概念、设计艺术和使用技巧三个方面,详细解析HashMap、TreeMap、LinkedHashMap等实现类,帮助您更好地理解和应用Java Map。
40 3
|
29天前
|
Java BI 调度
Java Spring的定时任务的配置和使用
遵循上述步骤,你就可以在Spring应用中轻松地配置和使用定时任务,满足各种定时处理需求。
123 1
|
6月前
|
消息中间件 SpringCloudAlibaba Java
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(八)Config服务配置+bus消息总线+stream消息驱动+Sleuth链路追踪
1008 0

推荐镜像

更多