/**
  * Perform a scan within the specified base packages, returning the registered bean definitions.
  *
  * <p>This method does <i>not</i> register an annotation config processor but rather leaves this
  * up to the caller.
  *
  * @param basePackages the packages to check for annotated classes
  * @return set of beans registered if any for tooling registration purposes (never {@code null})
  */
 protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
   mylog.debug("遍历basePackages,获取包路径下面的.class所有类");
   for (String basePackage : basePackages) {
     Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
     mylog.debug("获取到有注解的bean集合并遍历,每个bean设置scope");
     for (BeanDefinition candidate : candidates) {
       ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
       candidate.setScope(scopeMetadata.getScopeName());
       String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
       if (candidate instanceof AbstractBeanDefinition) {
         postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
       }
       if (candidate instanceof AnnotatedBeanDefinition) {
         AnnotationConfigUtils.processCommonDefinitionAnnotations(
             (AnnotatedBeanDefinition) candidate);
       }
       if (checkCandidate(beanName, candidate)) {
         BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
         definitionHolder =
             AnnotationConfigUtils.applyScopedProxyMode(
                 scopeMetadata, definitionHolder, this.registry);
         beanDefinitions.add(definitionHolder);
         mylog.debug("登记AnnotatedBeanDefinition类型的bean");
         registerBeanDefinition(definitionHolder, this.registry);
       }
     }
   }
   return beanDefinitions;
 }
  /**
   * Perform a scan within the specified base packages.
   *
   * @param basePackages the packages to check for annotated classes
   * @return number of beans registered
   */
  public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

    doScan(basePackages);

    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
      AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

    return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
  }
  /** Register the {@link Configuration} class itself as a bean definition. */
  private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
    AnnotationMetadata metadata = configClass.getMetadata();
    AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);

    ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
    configBeanDef.setScope(scopeMetadata.getScopeName());
    String configBeanName =
        this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
    AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);

    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
    definitionHolder =
        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    this.registry.registerBeanDefinition(
        definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
    configClass.setBeanName(configBeanName);

    if (logger.isDebugEnabled()) {
      logger.debug("Registered bean definition for imported class '" + configBeanName + "'");
    }
  }
  /**
   * Apply processing and build a complete {@link ConfigurationClass} by reading the annotations,
   * members and methods from the source class. This method can be called multiple times as relevant
   * sources are discovered.
   *
   * @param configClass the configuration class being build
   * @param sourceClass a source class
   * @return the superclass, or {@code null} if none found or previously processed
   */
  protected final SourceClass doProcessConfigurationClass(
      ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
    // Recursively process any member (nested) classes first
    processMemberClasses(configClass, sourceClass);

    // 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");
      }
    }

    // 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());
          }
        }
      }
    }

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

    // Process any @ImportResource annotations
    if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
      AnnotationAttributes importResource =
          AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
      String[] resources =
          importResource.getAliasedStringArray("locations", ImportResource.class, sourceClass);
      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
    Set<MethodMetadata> beanMethods =
        sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());
    for (MethodMetadata methodMetadata : beanMethods) {
      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // Process default methods on interfaces
    for (SourceClass ifc : sourceClass.getInterfaces()) {
      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));
        }
      }
    }

    // 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;
  }
  /**
   * Read the given {@link BeanMethod}, registering bean definitions with the BeanDefinitionRegistry
   * based on its contents.
   */
  private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    ConfigurationClass configClass = beanMethod.getConfigurationClass();
    MethodMetadata metadata = beanMethod.getMetadata();
    String methodName = metadata.getMethodName();

    // Do we need to mark the bean as skipped by its condition?
    if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
      configClass.skippedBeanMethods.add(methodName);
      return;
    }
    if (configClass.skippedBeanMethods.contains(methodName)) {
      return;
    }

    // Consider name and any aliases
    AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
    List<String> names = new ArrayList<String>(Arrays.asList(bean.getStringArray("name")));
    String beanName = (names.size() > 0 ? names.remove(0) : methodName);

    // Register aliases even when overridden
    for (String alias : names) {
      this.registry.registerAlias(beanName, alias);
    }

    // Has this effectively been overridden before (e.g. via XML)?
    if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
      return;
    }

    ConfigurationClassBeanDefinition beanDef =
        new ConfigurationClassBeanDefinition(configClass, metadata);
    beanDef.setResource(configClass.getResource());
    beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));

    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);
    }
    beanDef.setAutowireMode(RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);
    beanDef.setAttribute(
        RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);

    AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);

    Autowire autowire = bean.getEnum("autowire");
    if (autowire.isAutowire()) {
      beanDef.setAutowireMode(autowire.value());
    }

    String initMethodName = bean.getString("initMethod");
    if (StringUtils.hasText(initMethodName)) {
      beanDef.setInitMethodName(initMethodName);
    }

    String destroyMethodName = bean.getString("destroyMethod");
    if (destroyMethodName != null) {
      beanDef.setDestroyMethodName(destroyMethodName);
    }

    // Consider scoping
    ScopedProxyMode proxyMode = ScopedProxyMode.NO;
    // TODO Determine why type is hard coded to org.springframework.context.annotation.Scope,
    // since AnnotationScopeMetadataResolver supports a custom scope annotation type.
    AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
    if (attributes != null) {
      beanDef.setScope(
          attributes.getAliasedString("value", Scope.class, configClass.getResource()));
      proxyMode = attributes.getEnum("proxyMode");
      if (proxyMode == ScopedProxyMode.DEFAULT) {
        proxyMode = ScopedProxyMode.NO;
      }
    }

    // Replace the original bean definition with the target one, if necessary
    BeanDefinition beanDefToRegister = beanDef;
    if (proxyMode != ScopedProxyMode.NO) {
      BeanDefinitionHolder proxyDef =
          ScopedProxyCreator.createScopedProxy(
              new BeanDefinitionHolder(beanDef, beanName),
              this.registry,
              proxyMode == ScopedProxyMode.TARGET_CLASS);
      beanDefToRegister =
          new ConfigurationClassBeanDefinition(
              (RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);
    }

    if (logger.isDebugEnabled()) {
      logger.debug(
          String.format(
              "Registering bean definition for @Bean method %s.%s()",
              configClass.getMetadata().getClassName(), beanName));
    }

    this.registry.registerBeanDefinition(beanName, beanDefToRegister);
  }