Spring source code analysis - everything about Bean

Reading tips: TL;DR. The article contains a large number of source code. It takes a long time to read. It may take more than 20 minutes to read carefully.

Recall that the core function of spring is, after all, a container for providing so-called "beans" ", and is responsible for the connection between beans. As we know, beans have different scopes, i.e. scope, singleton, prototype, Session, or custom. Beans can also be lazy loaded. Therefore, the time to create a Bean may be any time at runtime. Spring uses BeanDefinition to describe metadata such as name, class, scope, etc. of a Bean and create it when necessary. The creation process naturally includes the process of automatic injection.

In this article, we focus on Bean management in Spring.

How to describe a Bean

Spring uses BeanDefinition to describe beans. As shown above, we only select a few representative ones to see. Based on the definition of BeanDefinition interface, we can know that beans in spring have the following characteristics:

  • essential information
    • name
    • describe
    • The Class object used to create the Bean
  • Scope with scope
    • singleton: globally unique instance
    • prototype: create a new instance every time you get it
  • You can have a parent-child relationship
  • There can be dependencies, that is, the creation of one Bean depends on the existence of another Bean
  • Delay loading
  • Automatic assembly
    • Set whether to participate in automatic assembly (based on type)
    • If more than one type meets the requirements, the main type can be set
  • You can customize how Bean instances are created
    • Set the factory Bean and create it with another Bean
    • Set the factory method and create an instance with it
  • Life cycle management
    • The initialization method initMethod can be specified
    • destroyMethod can be specified
  • There are roles. Currently, three roles are defined, but we haven't seen how to use them yet
    • ROLE_APPLICATION: This Bean is the main body of the application. The default value is
    • ROLE_SUPPORT: identifies that the Bean is the most other supported part
    • Role_infrastruture: the Bean is supported as a pure infrastructure and is inaccessible to users
  • Gets the value of the parameter of the constructor
  • Get MutablePropertyValues, which corresponds to the property key value pair of the Bean, which will be used during automatic injection.
  • BeanDefinition can wrap another BeanDefinition. Get the wrapped definition through BeanDefinition getoriginingbeandefinition().

As for other classes in the inheritance tree, they are different. Let's look at them in turn

AbstractBeanDefinition

The direct implementation of BeanDefinition, from which you can also see the default values of some features

  • scope defaults to single instance
  • Delay loading is off by default
  • Auto injection is off by default
  • The role defaults to ROLE_APPLICATION

RootBeanDefinition

To tell you the truth, I'm a little confused when I look at this class directly. That's what the original annotation said.

A root bean definition represents the merged bean definition that backs a specific bean in a Spring BeanFactory at runtime. It might have been created from multiple original bean definitions that inherit from each other, typically registered as GenericBeanDefinitions. A root bean definition is essentially the 'unified' bean definition view at runtime.
Root bean definitions may also be used for registering individual bean definitions in the configuration phase. However, since Spring 2.5, the preferred way to register bean definitions programmatically is the GenericBeanDefinition class. GenericBeanDefinition has the advantage that it allows to dynamically define parent dependencies, not 'hard-coding' the role as a root bean definition.

I understand the literal meaning, but I don't understand the meaning behind this string of English. What is MergedBean? You can refer to it for reference This article , the so-called merging means merging a parent-child BeanDefinition into a BeanDefinition. The specific process of "merging" will be analyzed in the following "how to create a Bean".

GenericBeanDefinition

Compared with AbstractBeanDefinition, the standard implementation of BeanDefinition has more implementation of parent-child relationship. We can directly use this class to create our own BeanDefinition. If we want to create a BeanDefinition injection container out of thin air, we can use it.

AnnotatedBeanDefinition

The direct extension interface of BeanDefinition exposes AnnotationMetadata to the caller. The problem is, what is AnnotationMetadata? It is the abstraction defined by Spring for the annotation of the specified class. Through it, annotation information can be obtained without loading the target class. Like ClassMetadata, it seems that Spring abstracts all aspects of Bean definition.

AnnotatedGenericBeanDefinition

At the same time, GenericBeanDefinition and AnnotatedBeanDefinition are implemented, which shows that it not only has the ability of a complete BeanDefinition, but also holds the annotation information on the original class of the Bean. What's the use of holding the annotation information? Of course, it is useful. The runtime can directly obtain the annotation solution of the Bean definition without loading the original class, so as to improve the performance.

ScannedGenericBeanDefinition

Like AnnotatedGenericBeanDefinition, as like as two peas.

Summary

Spring's description of beans is actually more than these categories, but I think the others are interference, so they are removed. Our focus is to see how spring abstracts and the role of abstractions at all levels through these definitions. It will not be used when writing code, but it can ensure that it will not be confused when looking at the source code.

Where does Bean definition come from

It is also the example of the previous article, the simplest application method.

fun main() {
    val context = AnnotationConfigApplicationContext("com.gitee.floyd.springme.core")
    println(context.getBean(Bean1::class.java))
}

We take the org.springframework.context.annotation.AnnotationConfigApplicationContext construction method as the entry to analyze the loading process of Bean definitions.

public AnnotationConfigApplicationContext(String... basePackages) {
  this();
  scan(basePackages);
  ... ...
}

public AnnotationConfigApplicationContext() {
  this.reader = new AnnotatedBeanDefinitionReader(this);
  this.scanner = new ClassPathBeanDefinitionScanner(this);
}


public void scan(String... basePackages) {
  ... ... 
    this.scanner.scan(basePackages);
  ... ...
}

Here we introduce two new classes: AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner. Let's look at their capabilities first, and then look at the scanning process

AnnotatedBeanDefinitionReader

public class AnnotatedBeanDefinitionReader {
  // Bean definition registrar is a registration interface. Its implementation class generally holds all these registered bean definitions. The typical implementation class is a specific ApplicationContext
  private final BeanDefinitionRegistry registry;
  // The Bean name generator, AnnotationBeanNameGenerator, generates the name according to the @ Component annotation or its sub annotation
  private BeanNameGenerator beanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;
  // Scope meta information parser, AnnotationScopeMetadataResolver parses @ scope annotations
  private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
  // Condition calculator for parsing @ Conditional annotation
  private ConditionEvaluator conditionEvaluator;

  ... ...
  // Register Bean
  public void registerBean(Class<?> beanClass) {
    doRegisterBean(beanClass, null, null, null, null);
  }
  ... ...
  // Actually register the bean, and the type and name of the incoming bean
  private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
                                    @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
                                    @Nullable BeanDefinitionCustomizer[] customizers) {
		// Create AnnotatedGenericBeanDefinition from class object
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
    // Determine whether the Bean definition needs to be ignored
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
      return;
    }
		// Set a callback function when creating an instance as a replacement for a factory method
    abd.setInstanceSupplier(supplier);
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    // Add Scope to Bean definition
    abd.setScope(scopeMetadata.getScopeName());
    // Generate bean name
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
		// Handling general annotations: @ Lazy, @ Primary, @ DependsOn, @ Role, @ Description
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    // Determined according to the passed in modifier annotation
    if (qualifiers != null) {
      for (Class<? extends Annotation> qualifier : qualifiers) {
        if (Primary.class == qualifier) {
          abd.setPrimary(true);
        }
        else if (Lazy.class == qualifier) {
          abd.setLazyInit(true);
        }
        else {
          abd.addQualifier(new AutowireCandidateQualifier(qualifier));
        }
      }
    }
    // Apply the incoming BeanDefinition customizer
    if (customizers != null) {
      for (BeanDefinitionCustomizer customizer : customizers) {
        customizer.customize(abd);
      }
    }
		
    // Packaged as BeanDefinitionHolder
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    // The scope proxy mode of Bean is encapsulated in BeanDefinitionHolder, which is set here
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    // Inject the processed BeanDefinitionHolder into the registry
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
  }
}

// AnnotationConfigUtils.processCommonDefinitionAnnotations method
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
  // Processing of delayed loading
  AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
  if (lazy != null) {
    abd.setLazyInit(lazy.getBoolean("value"));
  }
  else if (abd.getMetadata() != metadata) {
    lazy = attributesFor(abd.getMetadata(), Lazy.class);
    if (lazy != null) {
      abd.setLazyInit(lazy.getBoolean("value"));
    }
  }
	// primary processing
  if (metadata.isAnnotated(Primary.class.getName())) {
    abd.setPrimary(true);
  }
  // Dependent processing
  AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
  if (dependsOn != null) {
    abd.setDependsOn(dependsOn.getStringArray("value"));
  }
	// Role processing
  AnnotationAttributes role = attributesFor(metadata, Role.class);
  if (role != null) {
    abd.setRole(role.getNumber("value").intValue());
  }
  // Described processing
  AnnotationAttributes description = attributesFor(metadata, Description.class);
  if (description != null) {
    abd.setDescription(description.getString("value"));
  }
}

Summary of key points

  • AnnotatedBeanDefinitionReader encapsulates the process of building and injecting BeanDefinition into BeanDefinitionRegistry, where BeanDefinitionRegistry is our container
  • BeanDefinitionRegistry, which was introduced earlier in the introduction of ApplicationContext, is used to receive and hold Bean definitions
  • bean name generator: AnnotationBeanNameGenerator, which is generated according to @ Component and its sub annotations, @ ManagedBean, @ Named. See the following for detailed analysis
  • Scope meta information, including scope scope and proxy mode, is identified by @ scope annotation. AnnotationScopeMetadataResolver is used to parse @ scope annotation. If the @ scope annotation is not specified, the result is: Singleton + no proxy.
  • ConditionEvaluator, condition parser. Parse @ Conditional. The detailed analysis is shown below.

bean name generation logic

The AnnotationBeanNameGenerator class is worth a look. It has some hidden functions

public class AnnotationBeanNameGenerator implements BeanNameGenerator {
  // @The compelent annotation is presented in literal form
  private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";

  // Core method
  @Override
  public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    // If the Bean has an annotation, the name is generated based on the annotation
    if (definition instanceof AnnotatedBeanDefinition) {
      String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
      if (StringUtils.hasText(beanName)) {
        // Explicit bean name found.
        return beanName;
      }
    }
    // If the Bean has no annotation, the initial lowercase form of the class name is used directly
    return buildDefaultBeanName(definition, registry);
  }

  protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
    AnnotationMetadata amd = annotatedDef.getMetadata();
    Set<String> types = amd.getAnnotationTypes();
    String beanName = null;
    for (String type : types) {
      AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
      if (attributes != null) {
        // Get meta annotations, that is, annotations of annotations, so that annotations such as @ Service and @ Repository can be detected
        Set<String> metaTypes = this.metaAnnotationTypesCache.computeIfAbsent(type, key -> {
          Set<String> result = amd.getMetaAnnotationTypes(key);
          return (result.isEmpty() ? Collections.emptySet() : result);
        });
        if (isStereotypeWithNameValue(type, metaTypes, attributes)) {
          // The value attribute of the annotation must have a value and is not an empty string
          Object value = attributes.get("value");
          if (value instanceof String) {
            String strVal = (String) value;
            if (StringUtils.hasLength(strVal)) {
              if (beanName != null && !strVal.equals(beanName)) {
                throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
                                                "component names: '" + beanName + "' versus '" + strVal + "'");
              }
              beanName = strVal;
            }
          }
        }
      }
    }
    return beanName;
  }

  // 
  protected boolean isStereotypeWithNameValue(String annotationType, Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes) {
    // It can also be seen here that JSR specified annotations such as @ ManagedBean and Named are also supported
    boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
      metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) ||
      annotationType.equals("javax.annotation.ManagedBean") ||
      annotationType.equals("javax.inject.Named");

    return (isStereotype && attributes != null && attributes.containsKey("value"));
  }

  protected String buildDefaultBeanName(BeanDefinition definition) {
    String beanClassName = definition.getBeanClassName();
    Assert.state(beanClassName != null, "No bean class name set");
    String shortClassName = ClassUtils.getShortName(beanClassName);
    return Introspector.decapitalize(shortClassName);
  }
}

main points

  • Annotations that support the generation of Bean names include: @ Component and its sub annotations, @ MangedBean, @ Named
  • If these annotations do not explicitly specify the name, they fall back to the default rule: the class name is lowercase

Determine whether the BeanDefinition needs to be loaded

The ConditionEvaluator class is also worth a visit. It explains how the @ Conditional annotation works. The entire class exposes a method: shouldSkip()

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
  // If the target class is not conditionally annotated, it should not be skipped
  if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
    return false;
  }

  if (phase == null) {
  	// If the configuration process is not specified, judge whether it belongs to the configuration phase or the Bean creation phase according to other conditions
    if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
      return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
    }
    return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
  }

  List<Condition> conditions = new ArrayList<>();
  // Extract the Condition condition from the @ Condition annotation
  for (String[] conditionClasses : getConditionClasses(metadata)) {
    for (String conditionClass : conditionClasses) {
      Condition condition = getCondition(conditionClass, this.context.getClassLoader());
      conditions.add(condition);
    }
  }
	// sort
  AnnotationAwareOrderComparator.sort(conditions);

  for (Condition condition : conditions) {
    ConfigurationPhase requiredPhase = null;
    if (condition instanceof ConfigurationCondition) {
      requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
    }
    // When the phases match and the conditions do not match, the Bean registration is skipped
    if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
      return true;
    }
  }
	// In other cases, it should not be skipped
  return false;
}

Analyze ConfigurationClassUtils.isConfigurationCandidate()

private static final Set<String> candidateIndicators = new HashSet<>(8);

static {
  // The following four annotations illustrate the configuration of beans
  candidateIndicators.add(Component.class.getName());
  candidateIndicators.add(ComponentScan.class.getName());
  candidateIndicators.add(Import.class.getName());
  candidateIndicators.add(ImportResource.class.getName());
}

public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
  // Annotations on the interface are not considered
  if (metadata.isInterface()) {
    return false;
  }
	// As long as it is annotated by the above four annotations, it indicates that the Bean is configured
  for (String indicator : candidateIndicators) {
    if (metadata.isAnnotated(indicator)) {
      return true;
    }
  }
	// Otherwise, if there is a method annotated by @ Bean inside, the Bean is also configured
  return hasBeanMethods(metadata);
}

static boolean hasBeanMethods(AnnotationMetadata metadata) {
  try {
    return metadata.hasAnnotatedMethods(Bean.class.getName());
  }
  catch (Throwable ex) {
    ... ...
  }
}

Understanding key

  • AnnotatedTypeMetadata should be understood. As seen in the AnnotatedBeanDefinition earlier, Spring abstracts the annotation information and class information on the Bean, making it easy to use without loading specific classes.
  • Condition condition. It has only one matching method, boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata), which is used to match whether the specified Bean meets the conditions in the container. Our commonly used annotation such as @ ConditionalOnClassMissingBean is the combination of @ Conditional derived annotation + condition derived class.

Summary of key points

  • ConditionEvaluator is used to judge whether a Bean definition meets the loading conditions, whether it is loaded or not, and should be ignored. Judgment logic
    • If the @ Conditional annotation does not exist, it needs to be loaded
    • If there is a @ Conditional annotation, you need to look at the action stage of the annotation and the behavior of its internal Condition
      • If the phase specified by Condition is inconsistent with the phase actually used by @ Conditional, it needs to be loaded
      • Otherwise, determine whether to load according to the result of Condition.matches()
  • When a bean definition is annotated with @ Component, @ ComponentScan, @ Import, @ ImportResource, or contains @ bean annotation methods, it indicates that it is a configuration bean. That is, the action phase is ConfigurationPhase.PARSE_CONFIGURATION, otherwise, the active phase is ConfigurationPhase.REGISTER_BEAN, the action stage, can also be used to judge whether the bean definition needs to be loaded: when the action stage of the declaration is inconsistent with that of the Condition class, the matching process will be ignored and loaded directly

ClassPathBeanDefinitionScanner

The inheritance tree is as above

  • ClassPathScanningCandidateComponentProvider is used to scan and provide Bean definitions (so-called CandidateComponent) under the specified package. The core logic is in the public set < beandefinition > findcandidatecomponents (string basepackage) method.
  • ClassPathBeanDefinitionScanner is used to define beans under batch scanning packages. The core logic is in the protected set < beandefinitionholder > doscan (string... Basepackages) method.

According to the calling relationship, starting from the scan method

public int scan(String... basePackages) {
  // Scan: scan the BeanDefinition from the package and inject it into the registry. See the following for details
  doScan(basePackages);
  if (this.includeAnnotationConfig) {
    // Register a batch of postprocessors, which we ignore. It registers a batch of internal postprocessors
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
  }
  // return what doesn't matter
  return ...
}

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
  Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
  for (String basePackage : basePackages) {
    // Call findCandidateComponents of the parent class to find the qualified BeanDefinition under the specified package
    Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
    for (BeanDefinition candidate : candidates) {
      // Parsing Scope meta information
      ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
      candidate.setScope(scopeMetadata.getScopeName());
      // Generate bean name
      String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
      if (candidate instanceof AbstractBeanDefinition) {
        // A wave of additional logic, see below for details
        postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
      }
      if (candidate instanceof AnnotatedBeanDefinition) {
        // Handle a wave of general annotations: @ Lazy, @ Primary, @ DependsOn, @ Role, @ Description. This method has been analyzed earlier
        AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
      }
      // Check whether the newly created BeanDefinition is legal, mainly to check whether there is a Bean with the same name in the container
      if (checkCandidate(beanName, candidate)) {
        // Build BeanDefinitionHolder
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
        // Proxy mode to apply scope
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        beanDefinitions.add(definitionHolder);
        // Inject registry
        registerBeanDefinition(definitionHolder, this.registry);
      }
    }
  }
  return beanDefinitions;
}

protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
  // Application default settings: delayed loading, automatic injection mode, dependency check, initialization method, destruction method, etc
  beanDefinition.applyDefaults(this.beanDefinitionDefaults);
  if (this.autowireCandidatePatterns != null) {
    // Set whether to participate in automatic injection
    beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
  }
}

public void applyDefaults(BeanDefinitionDefaults defaults) {
  Boolean lazyInit = defaults.getLazyInit();
  if (lazyInit != null) {
    setLazyInit(lazyInit);
  }
  setAutowireMode(defaults.getAutowireMode());
  setDependencyCheck(defaults.getDependencyCheck());
  setInitMethodName(defaults.getInitMethodName());
  setEnforceInitMethod(false);
  setDestroyMethodName(defaults.getDestroyMethodName());
  setEnforceDestroyMethod(false);
}

There is the findCandidateComponents method calling ClassPathScanningCandidateComponentProvider above. Let's look again

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
  if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
    // Component scanning via Index
    return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
  } else {
    // Direct scanning
    return scanCandidateComponents(basePackage);
  }
}

// Direct scanning process:
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
  Set<BeanDefinition> candidates = new LinkedHashSet<>();
  try {
    // Path: classpath *: {path name after package conversion} / * * / *. Class, that is, all class files at all levels under the top package name under all jar packages
    String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern;
    Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
    for (Resource resource : resources) 
      if (resource.isReadable()) {
        try {
          MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
          // It must meet the filter requirements in includeFilters before it can be scanned
          if (isCandidateComponent(metadataReader)) {
            // The BeanDefinition just scanned has only created an instance of the Bean definition and has not started parsing. The parsing is left to subclasses.
            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
            sbd.setSource(resource);
            if (isCandidateComponent(sbd)) {
              candidates.add(sbd);
            }
          }
        } catch (Throwable ex) {
          throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);
        }
      }
    }
  } catch (IOException ex) {
    throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
  }
  return candidates;
}

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
  // In the discharge filter, it does not meet the requirements directly
  for (TypeFilter tf : this.excludeFilters) {
    if (tf.match(metadataReader, getMetadataReaderFactory())) {
      return false;
    }
  }
  // Match in the include filter to meet the requirements
  for (TypeFilter tf : this.includeFilters) {
    if (tf.match(metadataReader, getMetadataReaderFactory())) {
      return isConditionMatch(metadataReader);
    }
  }
  return false;
}

Only the code scanned directly from the class is posted above, but the other addcandidatecomponents fromindex does not say. We need to know what Index is in Spring first. Can refer to This article:

After @ Indexed is used in the project, the META-INT/spring.components file will be automatically generated in the project when compiling and packaging. When the Spring application context performs a ComponentScan, META-INT/spring.components will be read and loaded by the CandidateComponentsIndexLoader and converted into a CandidateComponentsIndex object. In this case, @ ComponentScan will not scan the specified package, but read the CandidateComponentsIndex object, so as to improve performance.

At this point, as like as two peas, we will follow the addCandidateComponentsFromIndex() method and find that it is exactly the same as what we said above.

Summary of key points

  • You can see that the ClassPathBeanDefinitionScanner does the same thing as the AnnotatedBeanDefinitionReader. It builds BeanDefinition and registers it with the container; The only difference is that the Bean definition of the former is scanned by itself, while the Bean definition of the latter is registered by external calling methods

  • Through ClassPathScanningCandidateComponentProvider.findCandidateComponents(), we know that it only provides the most original logic for scanning and generating BeanDefinition, and the resolution and setting of various properties of BeanDefinition are placed in its subclass ClassPathBeanDefinitionScanner.

Summary

This Reader and Scanner are just tools, and the BeanDefinition in the container is finally created.

How to create a Bean

Bean creation logic

There are several opportunities for Bean creation

  • For the non delayed loaded singleton Bean, it will be created when the container refresh es, as we saw in the ApplicationContext source code analysis
  • For deferred loaded singleton beans, they are created the first time they are retrieved
  • For the prototype Bean, it will be created every time it is obtained

Let's take a look at the first case. Go directly to org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons. I deleted the unimportant code.

public void preInstantiateSingletons() throws BeansException {
  // Iterate over a copy to allow for init methods which in turn register new bean definitions.
  // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
  List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

  // Trigger initialization of all non-lazy singleton beans...
  for (String beanName : beanNames) {
    // Get the merged BeanDefinition
    RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    // Singleton and non deferred loading will be created
    if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
      if (isFactoryBean(beanName)) {
        // The name of the factory Bean has a special prefix:&
        Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
        if (bean instanceof FactoryBean) {
          FactoryBean<?> factory = (FactoryBean<?>) bean;
          // Special handling for SmartFactoryBean
          boolean isEagerInit = (factory instanceof SmartFactoryBean && ((SmartFactoryBean<?>) factory).isEagerInit());
          if (isEagerInit) {
            // Really create beans
            getBean(beanName);
          }
        }
      } else {
        // Really create beans
        getBean(beanName);
      }
    }
  }

  for (String beanName : beanNames) {
    // Special handling for SmartInitializingSingleton
    Object singletonInstance = getSingleton(beanName);
    if (singletonInstance instanceof SmartInitializingSingleton) {
      SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
      smartSingleton.afterSingletonsInstantiated();
    }
  }
}

public Object getBean(String name) throws BeansException {
  return doGetBean(name, null, null, false);
}

/**
 * Really create beans
 * name: bean Name of
 * requiedType: The type of Bean you want to resolve
 * args: Parameters used to construct the method when creating the bean. If you just get the existing bean, you don't need to
 * typeCheckOnly: Only check whether the resulting bean matches the target type, not really mark creation
 **/
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) {
  // Name conversion: it mainly deals with aliases and obtains real names according to aliases
  String beanName = transformedBeanName(name);
  Object beanInstance;

  // Eagerly check singleton cache for manually registered singletons.
  // Get the registered singleton Bean instance from the singleton cache
  Object sharedInstance = getSingleton(beanName);
  if (sharedInstance != null && args == null) {
    // Create a Bean instance based on the shared instance. If the shared instance is a factoryBean, use it to create a Bean
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  } else {
    // Check if bean definition exists in this factory.
    BeanFactory parentBeanFactory = getParentBeanFactory();
    // If the Bean does not exist in the parent container, try to obtain the Bean from the parent container. The logic is consistent with this class.
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
      String nameToLookup = originalBeanName(name);
      if (parentBeanFactory instanceof AbstractBeanFactory) {
        return ((AbstractBeanFactory) parentBeanFactory).doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
      } else if (args != null) {
        // Delegation to parent with explicit args.
        return (T) parentBeanFactory.getBean(nameToLookup, args);
      } else if (requiredType != null) {
        // No args -> delegate to standard getBean method.
        return parentBeanFactory.getBean(nameToLookup, requiredType);
      } else {
        return (T) parentBeanFactory.getBean(nameToLookup);
      }
    }

    // Tag created
    if (!typeCheckOnly) {
      markBeanAsCreated(beanName);
    }

    // If it is not obtained from the cache or parent container, it will be recreated. Here, obtain the BeanDefinition for creating
    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
    // Guarantee initialization of beans that the current bean depends on.
    // Get dependency
    String[] dependsOn = mbd.getDependsOn();
    if (dependsOn != null) {
      for (String dep : dependsOn) {
        // If the current Bean is dependent on its dependency, it will generate a circular dependency and report an error
        if (isDependent(beanName, dep)) {
          throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
        }
        try {
          // The logic of creating the dependent Bean is still the logic of this method
          getBean(dep);
        } catch (NoSuchBeanDefinitionException ex) {
          throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
        }
      }
    }

    // Create bean instance.
    // If it is a singleton, get the shared instance from the cache. If there is no shared instance, create it
    if (mbd.isSingleton()) {
      sharedInstance = getSingleton(beanName, () -> {
        try {
          // Create Bean
          return createBean(beanName, mbd, args);
        } catch (BeansException ex) {
          // Explicitly remove instance from singleton cache: It might have been put there
          // eagerly by the creation process, to allow for circular reference resolution.
          // Also remove any beans that received a temporary reference to the bean.
          destroySingleton(beanName);
          throw ex;
        }
      });
      // Create a Bean instance based on the shared instance. If the shared instance is a factoryBean, use it to create a Bean
      beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    } else if (mbd.isPrototype()) {
      // If it is a prototype, a new one is created each time
      // It's a prototype -> create a new instance.
      Object prototypeInstance = null;
      try {
        // Callback before creation: mark that the bean is being created
        beforePrototypeCreation(beanName);
        // Create Bean
        prototypeInstance = createBean(beanName, mbd, args);
      } finally {
        // Callback after creation: mark that the bean has not been created
        afterPrototypeCreation(beanName);
      }
      // Create a Bean instance based on the shared instance. If the shared instance is a factoryBean, use it to create a Bean
      beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    } else {
      // scope other than prototype and singleton
      String scopeName = mbd.getScope();
      // Get the specified Scope from the scopes cache, and the object is a Bean holder
      Scope scope = this.scopes.get(scopeName);
      try {
        // There is only one instance of the whole scope. If there is no instance, it will be created.
        Object scopedInstance = scope.get(beanName, () -> {
          // Callback before creation: mark that the bean is being created
          beforePrototypeCreation(beanName);
          try {
            // Create Bean
            return createBean(beanName, mbd, args);
          } finally {
            // Callback after creation: mark that the bean has not been created
            afterPrototypeCreation(beanName);
          }
        });
        // Create a Bean instance based on the shared instance. If the shared instance is a factoryBean, use it to create a Bean
        beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
      } catch (IllegalStateException ex) {
        throw new ScopeNotActiveException(beanName, scopeName, ex);
      }
    }
  }

  // Make the final adjustment. If the instantiated type is inconsistent with the desired type, a type conversion is also required
  return adaptBeanInstance(name, beanInstance, requiredType);
}

<T> T adaptBeanInstance(String name, Object bean, @Nullable Class<?> requiredType) {
  // Check if required type matches the type of the actual bean instance.
  if (requiredType != null && !requiredType.isInstance(bean)) {
    try {
      Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
      if (convertedBean == null) {
        throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
      }
      return (T) convertedBean;
    } catch (TypeMismatchException ex) {
      throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
    }
  }
  return (T) bean;
}

Although the above is only the code for preloading the singleton Bean, the method doGetBean() is a general method for obtaining the Bean. You can summarize this:

  • To create a singleton Bean, first get it from the cache; If not, it is obtained from the parent container. If not, the creation logic is executed
  • If a Bean has a dependent Bean, the dependent Bean will be created first; If the dependent Bean depends on the original Bean, it constitutes a circular dependency, and Spring will throw an exception
  • scope is implemented in only three cases
    • Singleton: when the scope of the BeanDefinition has no value (that is, the default singleton) or is a singleton, it is a singleton, and the whole container maintains only one instance
    • Prototype: when the scope of the BeanDefinition is prototype, it is the prototype, and the logic of creating a new Bean will be executed every time
    • Others: when the scope of BeanDefinition is not any of the above, the implementation method is that the container maintains a Map named scopes, and the value of each entry is a scope object, which is a container, and the new Bean will be put into the container. Only one Bean exists for the same scope.
  • The created Bean instance cannot be used directly: if the expected type is specified, call the type converter maintained in the container for type conversion to get the final value

For Bean acquisition methods under normal conditions, such as org.springframework.context.support.AbstractApplicationContext#getBean(java.lang.String), the following are available

public Object getBean(String name) throws BeansException {
  assertBeanFactoryActive();
  return getBeanFactory().getBean(name);
}

public Object getBean(String name) throws BeansException {
  // You can see whether it calls doGetBean() or the general method
  return doGetBean(name, null, null, false);
}

You can see whether it calls doGetBean() or the general method, which is omitted here.

MergedBeanDefinition

First of all, there is no class or interface definition, but "how to describe Bean" mentioned the concept of merging Bean definitions, and the creation logic of Bean saw this concept again. What does it mean? Let's look at it from the org.springframework.beans.factory.support.AbstractBeanFactory#getMergedLocalBeanDefinition method

protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
  // Get the results from the cache first
  RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
  if (mbd != null && !mbd.stale) {
    return mbd;
  }
  return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}

protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd) throws BeanDefinitionStoreException {
  return getMergedBeanDefinition(beanName, bd, null);
}
/**
 * Real method to get mergedBeanDefinition
 * Parameter description
 * beanName: bean Name of
 * bd: bean Definition of
 * contaningBd: Contains the bean definition of the bean (the bean is an internal bean)
 **/
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd) throws BeanDefinitionStoreException {
  synchronized (this.mergedBeanDefinitions) {
    RootBeanDefinition mbd = null;
    RootBeanDefinition previous = null;

    // Check with full lock now in order to enforce the same merged instance.
    if (containingBd == null) {
      mbd = this.mergedBeanDefinitions.get(beanName);
    }

    if (mbd == null || mbd.stale) {
      previous = mbd;
      // If the current Bean definition does not have a parent Bean definition, a RootBeanDefinition is created
      if (bd.getParentName() == null) {
        // Use copy of given root bean definition.
        if (bd instanceof RootBeanDefinition) {
          mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
        } else {
          mbd = new RootBeanDefinition(bd);
        }
      } else {
        // Otherwise, if the Bean definition is a child Bean definition, it needs to be merged with the parent Bean definition
        // Child bean definition: needs to be merged with parent.
        BeanDefinition pbd;
        try {
          String parentBeanName = transformedBeanName(bd.getParentName());
          // Recursive logic obtains the parent Bean definition from the current container or parent container
          if (!beanName.equals(parentBeanName)) {
            pbd = getMergedBeanDefinition(parentBeanName);
          } else {
            BeanFactory parent = getParentBeanFactory();
            if (parent instanceof ConfigurableBeanFactory) {
              pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
            }
          }
        } catch (NoSuchBeanDefinitionException ex) {
          throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
                                                 "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
        }
        // Deep copy with overridden values.
        mbd = new RootBeanDefinition(pbd);
        // Overwrite the parent Bean definition with the child Bean definition to get a "merged" Bean definition
        mbd.overrideFrom(bd);
      }

      // Set default singleton scope, if not configured before.
      // The default setting is single instance
      if (!StringUtils.hasLength(mbd.getScope())) {
        mbd.setScope(SCOPE_SINGLETON);
      }

      // A bean contained in a non-singleton bean cannot be a singleton itself.
      // Let's correct this on the fly here, since this might be the result of
      // parent-child merging for the outer bean, in which case the original inner bean
      // definition will not have inherited the merged outer bean's singleton status.
      // The bean needs to be consistent with the scope of the bean containing it
      if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
        mbd.setScope(containingBd.getScope());
      }

      // Cache the merged bean definition for the time being
      // (it might still get re-merged later on in order to pick up metadata changes)
      if (containingBd == null && isCacheBeanMetadata()) {
        this.mergedBeanDefinitions.put(beanName, mbd);
      }
    }
    // The merged Bean definition can be marked as stale, indicating that it needs to be merged again. The merge logic is executed here
    if (previous != null) {
      copyRelevantMergedBeanDefinitionCaches(previous, mbd);
    }
    return mbd;
  }
}

private void copyRelevantMergedBeanDefinitionCaches(RootBeanDefinition previous, RootBeanDefinition mbd) {
  if (ObjectUtils.nullSafeEquals(mbd.getBeanClassName(), previous.getBeanClassName()) &&
      ObjectUtils.nullSafeEquals(mbd.getFactoryBeanName(), previous.getFactoryBeanName()) &&
      ObjectUtils.nullSafeEquals(mbd.getFactoryMethodName(), previous.getFactoryMethodName())) {
    ResolvableType targetType = mbd.targetType;
    ResolvableType previousTargetType = previous.targetType;
    if (targetType == null || targetType.equals(previousTargetType)) {
      mbd.targetType = previousTargetType;
      mbd.isFactoryBean = previous.isFactoryBean;
      mbd.resolvedTargetType = previous.resolvedTargetType;
      mbd.factoryMethodReturnType = previous.factoryMethodReturnType;
      mbd.factoryMethodToIntrospect = previous.factoryMethodToIntrospect;
    }
  }
}

The key is that the parent Bean definition is overwritten by the child Bean definition. See org.springframework.beans.factory.support.AbstractBeanDefinition#overrideFrom

// But you see, it doesn't seem to be any different. It's just the properties defined by a normal Bean
public void overrideFrom(BeanDefinition other) {
  if (StringUtils.hasLength(other.getBeanClassName())) {
    setBeanClassName(other.getBeanClassName());
  }
  if (StringUtils.hasLength(other.getScope())) {
    setScope(other.getScope());
  }
  setAbstract(other.isAbstract());
  if (StringUtils.hasLength(other.getFactoryBeanName())) {
    setFactoryBeanName(other.getFactoryBeanName());
  }
  if (StringUtils.hasLength(other.getFactoryMethodName())) {
    setFactoryMethodName(other.getFactoryMethodName());
  }
  setRole(other.getRole());
  setSource(other.getSource());
  copyAttributesFrom(other);

  if (other instanceof AbstractBeanDefinition) {
    AbstractBeanDefinition otherAbd = (AbstractBeanDefinition) other;
    if (otherAbd.hasBeanClass()) {
      setBeanClass(otherAbd.getBeanClass());
    }
    if (otherAbd.hasConstructorArgumentValues()) {
      getConstructorArgumentValues().addArgumentValues(other.getConstructorArgumentValues());
    }
    if (otherAbd.hasPropertyValues()) {
      getPropertyValues().addPropertyValues(other.getPropertyValues());
    }
    if (otherAbd.hasMethodOverrides()) {
      getMethodOverrides().addOverrides(otherAbd.getMethodOverrides());
    }
    Boolean lazyInit = otherAbd.getLazyInit();
    if (lazyInit != null) {
      setLazyInit(lazyInit);
    }
    setAutowireMode(otherAbd.getAutowireMode());
    setDependencyCheck(otherAbd.getDependencyCheck());
    setDependsOn(otherAbd.getDependsOn());
    setAutowireCandidate(otherAbd.isAutowireCandidate());
    setPrimary(otherAbd.isPrimary());
    copyQualifiersFrom(otherAbd);
    setInstanceSupplier(otherAbd.getInstanceSupplier());
    setNonPublicAccessAllowed(otherAbd.isNonPublicAccessAllowed());
    setLenientConstructorResolution(otherAbd.isLenientConstructorResolution());
    if (otherAbd.getInitMethodName() != null) {
      setInitMethodName(otherAbd.getInitMethodName());
      setEnforceInitMethod(otherAbd.isEnforceInitMethod());
    }
    if (otherAbd.getDestroyMethodName() != null) {
      setDestroyMethodName(otherAbd.getDestroyMethodName());
      setEnforceDestroyMethod(otherAbd.isEnforceDestroyMethod());
    }
    setSynthetic(otherAbd.isSynthetic());
    setResource(otherAbd.getResource());
  }
  else {
    getConstructorArgumentValues().addArgumentValues(other.getConstructorArgumentValues());
    getPropertyValues().addPropertyValues(other.getPropertyValues());
    setLazyInit(other.isLazyInit());
    setResourceDescription(other.getResourceDescription());
  }
}

Summary of key points: the so-called merging is the merging of beandefinitions with parent-child relationship. They are merged into one BeanDefinition. The merging logic is to overwrite the parent BeanDefinition with the child BeanDefinition, and the final result is represented by RootBeanDefinition.

Automatic injection

There is too much code for automatic injection, which is divided into several parts

So far, we haven't seen the execution logic of automatic injection, BeanPostProcessor, etc. they are all in org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object []), which was called by the source code of the Bean created earlier

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
  RootBeanDefinition mbdToUse = mbd;

  // Make sure bean class is actually resolved at this point, and
  // clone the bean definition in case of a dynamically resolved Class
  // which cannot be stored in the shared merged bean definition.
  Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
  if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
    mbdToUse = new RootBeanDefinition(mbd);
    mbdToUse.setBeanClass(resolvedClass);
  }

  // Prepare method overrides.
  // Never mind
  mbdToUse.prepareMethodOverrides();

  // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
  // In some cases, the obtained Bean may be a proxy. At this time, it is not necessary to really execute the Bean creation logic. The instantiaawarebeanpostprocessor is used to do this
  Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
  if (bean != null) {
    return bean;
  }
	// If there are no additional settings, you can really create beans
  Object beanInstance = doCreateBean(beanName, mbdToUse, args);
  return beanInstance;
}
  • The only thing to note here is that you can provide a proxy instance through the instantiaawarebeanpostprocessor, which provides convenience for test functions such as Spy, so that they can forge beans and build a test environment.

Actions before creating an instance

Yes, resolvebeforeinstance

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
  Object bean = null;
  if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
    // Make sure bean class is actually resolved at this point.
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      // Determine the type of Bean
      Class<?> targetType = determineTargetType(beanName, mbd);
      if (targetType != null) {
        // When the InstantiationAwareBeanPostProcessor is applied, agents are generally generated
        bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
        if (bean != null) {
          // If an agent is generated, it is regarded as instantiated and all post processors are applied. Note why the post processor is called here, because the proxy generated above will return directly and will not follow the bean creation logic
          bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
        }
      }
    }
    mbd.beforeInstantiationResolved = (bean != null);
  }
  return bean;
}

// Application InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation, which is specially used before instantiation, so that we can return the agent of a target object.
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
  for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
    Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
    if (result != null) {
      return result;
    }
  }
  return null;
}

// After instantiation, all post processors are called.
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
  Object result = existingBean;
  for (BeanPostProcessor processor : getBeanPostProcessors()) {
    Object current = processor.postProcessAfterInitialization(result, beanName);
    if (current == null) {
      return result;
    }
    result = current;
  }
  return result;
}
  • Note the same as above

Create instance operation overview

For the actual operation of creation, there are

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
  // Instantiate the bean.
  // The created Bean does not exist directly in the form of an object, but in the form of a BeanWrapper
  BeanWrapper instanceWrapper = null;
  if (mbd.isSingleton()) {
    // If it is a singleton, remove the BeanWrapper from the cache
    instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  }
  if (instanceWrapper == null) {
    // Real creation steps
    instanceWrapper = createBeanInstance(beanName, mbd, args);
  }
  Object bean = instanceWrapper.getWrappedInstance();
  Class<?> beanType = instanceWrapper.getWrappedClass();
  if (beanType != NullBean.class) {
    // Backfill final information
    mbd.resolvedTargetType = beanType;
  }

  // Allow post-processors to modify the merged bean definition.
  synchronized (mbd.postProcessingLock) {
    if (!mbd.postProcessed) {
      // Apply all mergedbeandefinitionpostprocessors
      applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
      mbd.postProcessed = true;
    }
  }

  // Eagerly cache singletons to be able to resolve circular references
  // even when triggered by lifecycle interfaces like BeanFactoryAware.
  // Exposing singleton Bean references in advance is mainly to solve the problem of circular references
  boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
  if (earlySingletonExposure) {
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
  }

  // Initialize the bean instance.
  Object exposedObject = bean;

  // Perform Bean injection
  populateBean(beanName, mbd, instanceWrapper);
  // Initialize Bean
  exposedObject = initializeBean(beanName, exposedObject, mbd);

  if (earlySingletonExposure) {
    // Circular dependency check: normally, the object exposed in advance and the initialized bean object should be one, otherwise it indicates that there is a problem
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
      if (exposedObject == bean) {
        exposedObject = earlySingletonReference;
      } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
        String[] dependentBeans = getDependentBeans(beanName);
        Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
        for (String dependentBean : dependentBeans) {
          if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
            actualDependentBeans.add(dependentBean);
          }
        }
        if (!actualDependentBeans.isEmpty()) {
          throw ... ...
        }
      }
    }
  }

  // Register bean as disposable.
  // Register the bean with the destroy callback of the corresponding Scope
  registerDisposableBeanIfNecessary(beanName, bean, mbd);

  return exposedObject;
}
  • Note 1: the steps of creating a Bean include creating, executing injection, and executing initialization
  • Note 2: the solution of circular reference
Solution of circular reference

It should be noted that there are two ways of circular reference

  • When Scope is a singleton, it should be sorted out exactly as the code in "overview of creating instance operations"

    First, Spring maintains three related singleton caches

    • singletonObjects: used to maintain singleton objects that have been created
    • earlySingletonObjects: used to maintain singleton objects that have been created but have not yet been automatically injected and initialized
    • singletonFactories: used to maintain factory instances generated by singleton objects

    Then let's look at the specific methods of injection. For example, injection according to name still calls the getBean method to obtain the Bean, in which the getSingleton method will be called to obtain the instance object

    • First, get the created singleton object from singletonObjects
    • Then try to get the singleton object created to general from earlySingletonObjects
    • Then try to create a singleton object with the corresponding singletonFactories. Note that the object will be added with earlySingletonObjects, so you can ignore singletonFactories for convenience
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
      // It has been simplified to this
      singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
        singletonObject = this.earlySingletonObjects.get(beanName);
        if (singletonObject == null) {
          ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
          if (singletonFactory != null) {
            singletonObject = singletonFactory.getObject();
            this.earlySingletonObjects.put(beanName, singletonObject);
            this.singletonFactories.remove(beanName);
          }
        }
      }
      return singletonObject;
    }
    

    Then look at what you did when you created the bean (intercepting the previous code): you added a singletonFactory. Combined with the previous analysis, you actually put the newly created bean that has not been injected with properties and initialized in earlySingletonObjects, so that you can directly obtain the bean if there is a circular dependency. Avoid dead loop.

    if (earlySingletonExposure) {
      // The getEarlyBeanReference method actually passes the incoming bean back directly
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    

    To sum up, the focus here is to separate the creation and initialization (including injection) of beans, and maintain a semi initialized state, so that beans in the semi initialized state can be injected.

    **However, this can only be done when it is setter injection, and what about constructor injection? * * in fact, when constructor injection, the current object has not been created and cannot have a semi initialized state, so constructor injection does not allow circular dependency.

  • When Scope is a prototype, refer to line AbstractBeanFactory.doCreate.273, where you can check whether a prototype Bean with the same name under the current thread is being created. Spring is a synchronization framework. When creating a prototype Bean, it is impossible for the same thread to create two prototype beans at the same time. Obviously, as stated in the note, circular reference of prototype beans is not allowed here.

    // Fail if we're already creating this bean instance:
    // We're assumably within a circular reference.
    if (isPrototypeCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
    }
    

    Why doesn't the prototype support circular dependency? Because Spring doesn't cache any state related to the prototype. Although it can also have intermediate states, Spring doesn't cache it. This is a scenario that can be implemented technically, but Spring's design idea does not allow it to exist: the so-called prototype creates a new Bean every time it gets a Bean. What should be obtained when circular dependency is used A Bean completely different from me.

What is BeanWrapper

This class is not for us to use directly, but flows inside the container. It is used to provide the analysis and operation of actual Bean instances, such as operation and query on attributes. It is only commonly used to implement the class BeanWrapperImpl. Understanding it is helpful to view the source code. Let's take a brief look at the interface definition here

public interface BeanWrapper extends ConfigurablePropertyAccessor {
	// Get the real Bean instance
	Object getWrappedInstance();
	// Get the real Bean class
	Class<?> getWrappedClass();
	// Gets the property descriptor for all instances
	PropertyDescriptor[] getPropertyDescriptors();
	// Gets the property descriptor of the specified property
	PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;
}

Exact operation of creating an instance

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
  // Make sure bean class is actually resolved at this point.
  // Parsing real bean class objects
  Class<?> beanClass = resolveBeanClass(mbd, beanName);

  // If BeanDefinitino provides a Supplier for instance creation, it can be directly used to create it. The application scenario of Supplier needs to be explored
  Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
  if (instanceSupplier != null) {
    return obtainFromSupplier(instanceSupplier, beanName);
  }

  // If a factory method is specified, it is created directly through the factory method
  if (mbd.getFactoryMethodName() != null) {
    return instantiateUsingFactoryMethod(beanName, mbd, args);
  }

  // Shortcut when re-creating the same bean...
  boolean resolved = false;
  boolean autowireNecessary = false;
  if (args == null) {
    synchronized (mbd.constructorArgumentLock) {
      if (mbd.resolvedConstructorOrFactoryMethod != null) {
        resolved = true;
        autowireNecessary = mbd.constructorArgumentsResolved;
      }
    }
  }
  // If the constructors of BeanDefinition have been resolved, they can be directly used to create them
  if (resolved) {
    if (autowireNecessary) {
      // It is created by the constructor and injected with constructor parameters
      return autowireConstructor(beanName, mbd, null, null);
    } else {
      // Create directly using a parameterless constructor
      return instantiateBean(beanName, mbd);
    }
  }

  // If a SmartInstantiationAwareBeanPostProcessor provides a constructor, it is used to create
  Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
  if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
      mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
    return autowireConstructor(beanName, mbd, ctors, args);
  }

  // Create with default constructor
  ctors = mbd.getPreferredConstructors();
  if (ctors != null) {
    return autowireConstructor(beanName, mbd, ctors, null);
  }

  // Create directly using a parameterless constructor
  return instantiateBean(beanName, mbd);
}
  • Order in which alternatives are created
    1. Supplier set in advance
    2. Specified factory method
    3. Use the constructor parsed in advance by BeanDefinition to create
    4. Created using the constructor provided by SmartInstantiationAwareBeanPostProcessor
    5. It is created using the class's own parameterized constructor. The parameters come from the container and are automatically injected
    6. Create using the class's own parameterless constructor
Inject and create by a parametric constructor
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
  BeanWrapperImpl bw = new BeanWrapperImpl();
  // Initialization here only injects the content related to type conversion
  this.beanFactory.initBeanWrapper(bw);

  Constructor<?> constructorToUse = null;
  ArgumentsHolder argsHolderToUse = null;
  Object[] argsToUse = null;

  if (explicitArgs != null) {
    // Give args and use it directly
    argsToUse = explicitArgs;
  } else {
    // Without giving args, find args in BeanDefinition and use it
    Object[] argsToResolve = null;
    synchronized (mbd.constructorArgumentLock) {
      constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
      if (constructorToUse != null && mbd.constructorArgumentsResolved) {
        // Found a cached constructor...
        argsToUse = mbd.resolvedConstructorArguments;
        if (argsToUse == null) {
          argsToResolve = mbd.preparedConstructorArguments;
        }
      }
    }
    if (argsToResolve != null) {
      argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
    }
  }

  if (constructorToUse == null || argsToUse == null) {
    // Take specified constructors, if any.
    Constructor<?>[] candidates = chosenCtors;
    // Gets all constructor methods of the class as candidates
    if (candidates == null) {
      Class<?> beanClass = mbd.getBeanClass();
      candidates = (mbd.isNonPublicAccessAllowed() ? beanClass.getDeclaredConstructors() : beanClass.getConstructors());
    }
    // If the object has only one parameterless constructor, you can create the object directly without automatic injection
    if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
      Constructor<?> uniqueCandidate = candidates[0];
      if (uniqueCandidate.getParameterCount() == 0) {
        synchronized (mbd.constructorArgumentLock) {
          mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
          mbd.constructorArgumentsResolved = true;
          mbd.resolvedConstructorArguments = EMPTY_ARGS;
        }
        bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
        return bw;
      }
    }

    // Need to resolve the constructor.
    // Criteria for automatic injection: there is a constructor, or the injection mode of BeanDefinition is constructor injection
    boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
    ConstructorArgumentValues resolvedValues = null;

    int minNrOfArgs;
    if (explicitArgs != null) {
      minNrOfArgs = explicitArgs.length;
    } else {
      ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
      resolvedValues = new ConstructorArgumentValues();
      minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
    }

    AutowireUtils.sortConstructors(candidates);
    int minTypeDiffWeight = Integer.MAX_VALUE;
    Set<Constructor<?>> ambiguousConstructors = null;
    Deque<UnsatisfiedDependencyException> causes = null;
    // Find out which constructor to use to create the Bean
    for (Constructor<?> candidate : candidates) {
      int parameterCount = candidate.getParameterCount();

      ArgumentsHolder argsHolder;
      Class<?>[] paramTypes = candidate.getParameterTypes();
      if (resolvedValues != null) {
        String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
        if (paramNames == null) {
          ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
          if (pnd != null) {
            paramNames = pnd.getParameterNames(candidate);
          }
        }
        // Here, find the bean s that meet the requirements in the container and build the parameters that meet the requirements for later use
        argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
      } else {
        // Explicit arguments given -> arguments length must match exactly.
        if (parameterCount != explicitArgs.length) {
          continue;
        }
        argsHolder = new ArgumentsHolder(explicitArgs);
      }
			// Weight calculation rule: compare the given parameter type with the parameter at the corresponding position of the actual constructor. The more consistent it is, the higher the score will be.
      int typeDiffWeight = (mbd.isLenientConstructorResolution() ?  argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
      // Choose this constructor if it represents the closest match.
      // Bubble method to find the one that best meets the requirements. If the two weights are consistent, add the "indistinguishable" list
      if (typeDiffWeight < minTypeDiffWeight) {
        constructorToUse = candidate;
        argsHolderToUse = argsHolder;
        argsToUse = argsHolder.arguments;
        minTypeDiffWeight = typeDiffWeight;
        ambiguousConstructors = null;
      } else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
        if (ambiguousConstructors == null) {
          ambiguousConstructors = new LinkedHashSet<>();
          ambiguousConstructors.add(constructorToUse);
        }
        ambiguousConstructors.add(candidate);
      }
    }

    // Finally, no constructor is selected and an error is reported
    if (constructorToUse == null) {
      throw new BeanCreationException(......);
    } else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
      // Finally, multiple constructors meet the conditions, which cannot be distinguished, and an error is also reported
      throw new BeanCreationException(......);
    }
  }

  Assert.state(argsToUse != null, "Unresolved constructor arguments");
  // Create Bean instances with reflection
  bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
  return bw;
}
  • Spring will first find out the parameter combinations that meet the type requirements from the container, and then find the constructor that best matches these parameter types to create an instance
  • It is conceivable that as long as a bean of exact type is created, it will be instantiated successfully, because there can be no constructor with exactly the same parameter type
Create using a parameterless constructor
protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
  // There's no need to catch up here. It just gets the parameterless constructor of the class and instantiates it through reflection
  Object beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
  // Wrap as BeanWrapper
  BeanWrapper bw = new BeanWrapperImpl(beanInstance);
  initBeanWrapper(bw);
  return bw;
}

Execution of automatic injection logic

For the logic of automatic injection, i.e. populateBean(), there are

// Perform Bean injection
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
  // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
  // state of the bean before properties are set. This can be used, for example,
  // to support styles of field injection.
  if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
    // The instantiaawarebeanpostprocessor is applied and executed just after the instance is created and before the property is set
    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
      if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
        return;
      }
    }
  }

  PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

  // Self defined injection mode: automatic detection, by name and by type;
  int resolvedAutowireMode = mbd.getResolvedAutowireMode();
  if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
    MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
    // Add property values based on autowire by name if applicable.
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
      // Inject by name
      autowireByName(beanName, mbd, bw, newPvs);
    }
    // Add property values based on autowire by type if applicable.
    if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
      // Injection by type
      autowireByType(beanName, mbd, bw, newPvs);
    }
    pvs = newPvs;
  }

  boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
  boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

  PropertyDescriptor[] filteredPds = null;
  if (hasInstAwareBpps) {
    if (pvs == null) {
      pvs = mbd.getPropertyValues();
    }
    // Apply the instantiawarebeanpostprocessor.postprocessproperties and postProcessPropertyValues methods
    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
      PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
      if (pvsToUse == null) {
        if (filteredPds == null) {
          filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        }
        pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
        if (pvsToUse == null) {
          return;
        }
      }
      pvs = pvsToUse;
    }
  }
  if (needsDepCheck) {
    if (filteredPds == null) {
      filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
    }
    // Dependency check: check whether all required properties have been set
    checkDependencies(beanName, mbd, filteredPds, pvs);
  }
  if (pvs != null) {
    // Really apply these attributes
    applyPropertyValues(beanName, mbd, bw, pvs);
  }
}

// Name injection is to find the bean with the specified name from the container and add it to the temporary property set for later use
protected void autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
  String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
  for (String propertyName : propertyNames) {
    if (containsBean(propertyName)) {
      // Gets the Bean with the specified name
      Object bean = getBean(propertyName);
      pvs.add(propertyName, bean);
      // Register Bean dependency cache
      registerDependentBean(propertyName, beanName);
    }
  }
}

// Injection by type is a little more complicated. It is roughly to find all bean s that match the type from the container, and then get the exact one according to certain rules
protected void autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

  TypeConverter converter = getCustomTypeConverter();
  if (converter == null) {
    converter = bw;
  }

  Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
  String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
  for (String propertyName : propertyNames) {
    PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
    // Don't try autowiring by type for type Object: never makes sense,
    // even if it technically is a unsatisfied, non-simple property.
    if (Object.class != pd.getPropertyType()) {
      // Get the set method of the property
      MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
      // Do not allow eager init for type matching in case of a prioritized post-processor.
      boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
      DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
      Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
      if (autowiredArgument != null) {
        pvs.add(propertyName, autowiredArgument);
      }
      for (String autowiredBeanName : autowiredBeanNames) {
        // Register Bean dependency cache
        registerDependentBean(autowiredBeanName, beanName);
      }
      autowiredBeanNames.clear();
    }
  }
}

Operation of instance initialization

The logic of initialization is

// Initialize Bean
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
  // Call xxxAware and inject the corresponding content. There are three managed here: BeanNameAware, BeanClassLoaderAware and beanfactory aware
  invokeAwareMethods(beanName, bean);
  Object wrappedBean = bean;
  if (mbd == null || !mbd.isSynthetic()) {
    // Apply all BeanPostProcessor.postProcessBeforeInitialization methods
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  }
  // Call the initialization method, which includes initializingbean.afterpropertieset and the user-defined init method
  invokeInitMethods(beanName, wrappedBean, mbd);
  if (mbd == null || !mbd.isSynthetic()) {
    // Apply all BeanPostProcessor.postProcessAfterInitialization methods
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  }

  return wrappedBean;
}
  • Instantiation contains content
    • Call of beanxaware interface
    • Call to BeanPostProcessor.postProcessBeforeInitialization
    • Call to initializingbean.afterpropertieset
    • Call of custom initialization method
    • Call to BeanPostProcessor.postProcessAfterInitialization

Get the operation of the dependent instance according to the dependency type

When executing automatic injection logic, the following methods are useful. Let's see what it does: org.springframework.beans.factory.config.autowirecapablebeanfactory#resolvedependency (org.springframework.beans.factory.config.dependencydescriptor, java.lang.string, java.util.set < Java. Lang.string >, org.springframework.beans.typeconverter)

javaxInjectProviderClass = ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader());
/**
 * In the current container, resolve the Bean corresponding to the specified dependency
 * desciptor: Dependency description class, which can be field, method and constructor dependency. The description class contains these parameter information
 * requestingBeanName: The name of the bean that declares the dependency
 * autowiredBeanNames: A place to put the results, which is used to store the name of the found dependent bean
 * typeConverter: Type converter required
 * Return value: returns the dependent Bean
 **/
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
  // Initialize parameter name searcher
  descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
  if (Optional.class == descriptor.getDependencyType()) {
    // If the dependent type is Optional, it is created according to the corresponding logic
    return createOptionalDependency(descriptor, requestingBeanName);
  } else if (ObjectFactory.class == descriptor.getDependencyType() || ObjectProvider.class == descriptor.getDependencyType()) {
    // If the dependent type is factory or Provider (which is also a kind of factory), DependencyObjectProvider is returned
    return new DependencyObjectProvider(descriptor, requestingBeanName);
  } else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
    // If the dependent type is javax.inject.Provider, it will be resolved according to JSR330
    return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
  } else {
    // If deferred creation is supported, the resolution result is a proxy
    Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);
    if (result == null) {
      // This is the real creation
      result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
    }
    return result;
  }
}

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
  // Gets the type of the dependency
  Class<?> type = descriptor.getDependencyType();
  // Get the value explicitly specified during user setting injection, such as the value in @ Qualifier. The suggestion here means user suggestion, not calculated automatically
  Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
  if (value != null) {
    // If the recommended value is a string, parse the string and obtain the container of the corresponding name after parsing from the container
    if (value instanceof String) {
      // Parse the string, which means that the string can be a SPEL expression
      String strVal = resolveEmbeddedValue((String) value);
      BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
      // Get the final value. The BeanDefinition here is only passed in to get the scope value
      value = evaluateBeanDefinitionString(strVal, bd);
    }
    TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
    // Type conversion
    return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
  }
  // Try to parse the Bean as a collection type
  Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
  if (multipleBeans != null) {
    return multipleBeans;
  }
  // Get qualified bean s from the entire container
  Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
  if (matchingBeans.isEmpty()) {
    if (isRequired(descriptor)) {
      raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
    }
    // Still can't find, it's null
    return null;
  }

  String autowiredBeanName;
  Object instanceCandidate;

  // If the number of matched bean s is greater than 1, you need to make a selection
  if (matchingBeans.size() > 1) {
    // First, select according to the Primary, then sort according to the Priority, and finally select according to whether the found name is consistent with the dependent name
    autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
    // When there is no Primary, Priority is not implemented, and the name does not match, it may not be found
    if (autowiredBeanName == null) {
      if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
        // It is possible that descriptors have their own defined response methods
        return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
      } else {
        // In case of an optional Collection/Map, silently ignore a non-unique case:
        // possibly it was meant to be an empty collection of multiple regular beans
        // (before 4.3 in particular when we didn't even look for collection beans).
        // No descriptors, then null
        return null;
      }
    }
    // This is the match
    instanceCandidate = matchingBeans.get(autowiredBeanName);
  } else {
    // We have exactly one match.
    // When there happens to be a match, everything is beautiful
    Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
    autowiredBeanName = entry.getKey();
    instanceCandidate = entry.getValue();
  }

  if (autowiredBeanNames != null) {
    // Put the found bean name into the receive collection
    autowiredBeanNames.add(autowiredBeanName);
  }
  if (instanceCandidate instanceof Class) {
    // The final instance is obtained by parsing
    instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
  }
  Object result = instanceCandidate;

  return result;
}

protected Map<String, Object> findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
  // Get all bean names of the specified type in the current container and all parent containers as candidates
  String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());
  Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);
  for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
    Class<?> autowiringType = classObjectEntry.getKey();
    if (autowiringType.isAssignableFrom(requiredType)) {
      Object autowiringValue = classObjectEntry.getValue();
      autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
      if (requiredType.isInstance(autowiringValue)) {
        result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
        break;
      }
    }
  }
  for (String candidate : candidateNames) {
    // If these candidate bean s turn on the auto injection switch
    if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
      // Join candidate
      addCandidateEntry(result, candidate, descriptor, requiredType);
    }
  }
  ... ...
    return result;
}
private void addCandidateEntry(Map<String, Object> candidates, String candidateName, DependencyDescriptor descriptor, Class<?> requiredType) {
  // The following are the different parsing methods of different types of descriptors, which are not discussed here.
  if (descriptor instanceof MultiElementDescriptor) {
    Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
    if (!(beanInstance instanceof NullBean)) {
      candidates.put(candidateName, beanInstance);
    }
  } else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor && ((StreamDependencyDescriptor) descriptor).isOrdered())) {
    Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
    candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
  } else {
    candidates.put(candidateName, getType(candidateName));
  }
}

Summary

The creation of Bean is the most important thing, because it is related to the life cycle of Spring, which is a question asked very frequently in the interview. With the above analysis, we can summarize the key processes in the creation process. In fact, there are two case s

case1

  1. First, execute instantiawarebeanpostprocessor. Postprocessbeforeinstance() to provide an opportunity to create an agent. If the agent is created successfully
  2. Execute all BeanPostProcessor.postProcessAfterInitialization() methods
  3. For preloaded singleton beans, SmartInitializingSingleton.afterSingletonsInstantiated() is also called
  4. end

case2

  1. Instantiate Bean
    1. Attempt to create using the specified factory method
    2. Try to create with a construction method. If a parametric construction method is used, the parameters of the construction method will be automatically injected
  2. Apply instantiaawarebeanpostprocessor. Postprocessafterinstance()
  3. Automatically inject the required attributes, find qualified beans from the container by name or type, and inject
  4. Call BeanNameAware, BeanClassLoaderAware, beanfactory aware
  5. Apply BeanPostProcessor.postProcessBeforeInitialization()
  6. Apply initializingbean. Afterpropertieset()
  7. Apply custom init method
  8. Apply BeanPostProcessor.postProcessAfterInitialization
  9. For preloaded singleton beans, SmartInitializingSingleton.afterSingletonsInstantiated() is also called
  10. end

Note that the life cycle of beans in Spring is discussed here, not the life cycle of Spring. If it is the latter, please refer to the first article for analysis.

How to destroy beans

Objects in the JVM are recycled through reachability analysis and garbage collection mechanism; Bean objects in Spring are always held by containers. Can't they never be garbage collected? The idea is correct and the design is reasonable. However, it should be noted that there is a premise: Scope. Because of its existence, bean life cycle management becomes convenient.

  • For beans whose Scope is a singleton, the container is globally unique and referenced by the container. Of course, it will not and cannot be destroyed
  • For beans with Scope as the prototype, there is no reference inside the container after creation and they are handed over to the application, which is consistent with the objects from ordinary new and can be recycled
  • For other beans with a Scope, it depends on the Scope. The Scope is destroyed and the Bean objects are recycled together. This situation has not been seen before; Otherwise, it will be left to the Scope to deal with it. For example, the Scope of the Request or Session Scope in the web saves the Bean object in HttpServletRequest or HttpSession, that is, the life cycle ends with the destruction of the Request or Session.

We focus on two points: how to destroy beans destroyed with containers; How to destroy beans whose life cycle does not follow the container

Destroyed by container

Destruction method of container

protected void doClose() {
  // Check whether an actual close attempt is necessary...
  if (this.active.get() && this.closed.compareAndSet(false, true)) {
    // Publish container close event
    publishEvent(new ContextClosedEvent(this));

    // Call the shutdown method of the lifecycle processor
    if (this.lifecycleProcessor != null) {
      this.lifecycleProcessor.onClose();
    }

    // Play: destroy Bean
    destroyBeans();

    // Close the state of this context itself.
    // Actually, I didn't do anything
    closeBeanFactory();

    // Let subclasses do some final clean-up if they wish...
    // Container lifecycle approach
    onClose();

    // Switch to inactive.
    this.active.set(false);
  }
}

protected void destroyBeans() {
  getBeanFactory().destroySingletons();
}

public void destroySingletons() {
  String[] disposableBeanNames;
  synchronized (this.disposableBeans) {
    disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
  }
  for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
    destroySingleton(disposableBeanNames[i]);
  }
}

public void destroySingleton(String beanName) {
  ... ... 
  destroyBean(beanName, disposableBean);
}

protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
  ... ...
  bean.destroy();
  ... ...
}

You can see that when the container is closed, only the singleton Bean is destroyed and two life cycle related methods are called

  • LifyCycle.close()
  • DisposableBean.destroy()

But there is another kind of Bean destruction callback that we haven't seen: the call of custom destruction method

Destroyed by JVM

Just like the annotation on the annotation method org.springframework.context.annotation.Bean#destroyMethod, only beans whose life cycle is completely controlled by the container can normally be called by the container to various destruction methods, that is, single cases, which can not be guaranteed by other scopes. Therefore, life cycle methods such as prototypes and sessions mentioned above cannot be called normally, because the container can't control them.

Note: Only invoked on beans whose lifecycle is under the full control of the factory, which is always the case for singletons but not guaranteed for any other scope.

summary

This article has been written for three days. Can you believe it???

To sum up, from the perspective of source code, this paper introduces how Spring describes beans, how to scan beans when creating containers, how to create beans at different times, the solution of Bean circular dependency, the logic of automatic injection, and the destruction scenarios of beans with different scopes. But it's just a casual observation, but when you encounter problems in the future, you should be able to locate the problems soon 🤔.

Naming rules

  • Xxxprovider: Provider is a combination of policy pattern and abstract factory pattern. The so-called abstract factory pattern means that it encapsulates the process of creating instances; The so-called policy pattern means that it can be passed as a policy into other other other based classes. For example, classpathscanningandidatecomponentprovider is associated with AnnotationConfigApplicationContext.

What's next

Spring source code analysis - powerful BeanPostProcessor

Keywords: Java Spring Back-end

Added by -Zeus- on Sun, 21 Nov 2021 06:05:19 +0200