Spring IoC source code parsing registration configuration class

Entry code display

public AnnotationConfigApplicationContext(Class<? >... Constructor () {/** * () {/** * () {/** * () {/** * (); */ this(); /** * Register beans to Spring's container * part ②!! */ register(componentClasses); /** * Initialize spring's environment * part 3!! */ refresh(); }Copy the code

Register (componentClasses) register(componentClasses)

Register configuration classes

/ * ** Register a single bean to the container* You can use this method if you have a new class* However, after registration, you need to manually invoke the refresh() method to trigger container interpretation comments * * You can register a configuration class* You can also register a bean* / @Override public void register(Class
                ... componentClasses) {  Assert.notEmpty(componentClasses, "At least one component class must be specified");  this.reader.register(componentClasses); } Copy the code

This.reader. Register (componentClasses) is used to register the configuration class. Here the reader object is mentioned in an article on the bean’s reader AnnotatedBeanDefinitionReader.

public void register(Class
       ... componentClasses) {
    for(Class<? > componentClass : componentClasses) {        registerBean(componentClass);
    }
}
Copy the code

Get acquainted with Spring’s doXXX method

The real way to do things in Spring is to start with do. You’ll see many of these methods in subsequent methods. Let’s first look at the doRegisterBean() method implementation:

<T> void doRegisterBean(Class<T> beanClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
        @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
    / ** create a AnnotatedGenericBeanDefinition according to the specified bean* this AnnotatedGenericBeanDefinition can be interpreted as a data structure,* This structure contains some description of the class. Scope, lazy, etc* /  AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);  if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {  return;  }   abd.setInstanceSupplier(instanceSupplier);  ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);  / ** Sets the scope of the class* /  abd.setScope(scopeMetadata.getScopeName());  / ** Generate a beanName with the beanNameGenerator* / String beanName = (name ! =null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));   / ** Handle common annotations in the class* The processed data is stored in ABD* /  AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);   / ** Handle qualifiers when they are not null* /  if(qualifiers ! =null) {  for (Class<? extends Annotation> qualifier : qualifiers) {  /** if set@PrimarySet its value to true */  if (Primary.class == qualifier) {  abd.setPrimary(true);  }  else if (Lazy.class == qualifier) {  /** if set@LazySet its value to true */  abd.setLazyInit(true);  }  else {  / * ** If other annotations are used, add a qualifier to the bean that is automatically assembled by name* /  abd.addQualifier(new AutowireCandidateQualifier(qualifier));  }  }  }  for (BeanDefinitionCustomizer customizer : definitionCustomizers) {  customizer.customize(abd);  }   / ** BeanDefinitionHolder is also a data structure* Associate beanName with ABD* /  BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);   / ** ScopedProxyMode to understand with web* /  definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);  / ** Register definitionHolder with Registry* registry is AnnotationConfigApplicationContext* AnnotationConfigApplicationContext when initialized by calling the superclass constructor instantiates a DefaultListableBeanFactory * * registerBeanDefinition definitionHolder is registered to the DefaultListableBeanFactory* /  BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); } Copy the code

Registers the configuration class sequence diagram

Sequence diagram

If you define beanDefinitionMap for MyConfig, add one more object to beanDefinitionMap. If you define beanDefinitionMap for MyConfig, add one more object to beanDefinitionMap.

1. Handle general annotations

/ * ** Common annotations for handling classes* @paramDescription class for beans in ABD Spring* @paramMetadata retrieves the metadata information about a bean from its description class in Spring* * After processing the general annotation, the information is put back into spring's bean description class.* / static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {  / * ** handle@Lazyannotations* /  AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);  if(lazy ! =null) {  /** Set the lazy loading information for the bean */  abd.setLazyInit(lazy.getBoolean("value"));  }  else if(abd.getMetadata() ! = metadata) { lazy = attributesFor(abd.getMetadata(), Lazy.class);  if(lazy ! =null) {  abd.setLazyInit(lazy.getBoolean("value"));  }  }   / * ** handle@Primaryannotations* /  if (metadata.isAnnotated(Primary.class.getName())) {  abd.setPrimary(true);  }   / * ** handle@DependsOnannotations* /  AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);  if(dependsOn ! =null) {  abd.setDependsOn(dependsOn.getStringArray("value"));  }   / * ** handle@Roleannotations* /  AnnotationAttributes role = attributesFor(metadata, Role.class);  if(role ! =null) {  abd.setRole(role.getNumber("value").intValue());  }   / * ** handle@Descriptionannotations* /  AnnotationAttributes description = attributesFor(metadata, Description.class);  if(description ! =null) {  abd.setDescription(description.getString("value"));  } } Copy the code

2.definitionHolderRegistered toregistry

Registry is AnnotationConfigApplicationContext because AnnotationConfigApplicationContext is BeanDefinitionRegistry implementation class.

public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
        throws BeanDefinitionStoreException {

    // Register bean definition under primary name.
 /** Get beanName */  String beanName = definitionHolder.getBeanName();  / * ** Register beanDefinition, place beanName and beanDefinition in the Map* /  registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());   // Register aliases for bean name, if any.  /** Handles alias */  String[] aliases = definitionHolder.getAliases();  if(aliases ! =null) {  for (String alias : aliases) {  registry.registerAlias(beanName, alias);  }  } } Copy the code

Container formation diagram

Ioc container composition

As you can see from the above process, the defined configuration classes are loaded into the container, so there is one more object in the container. Here is a brief description of BeanDefinitionMap keys for objects in containers.

  • The inside of the SpringBeanDefinitionnameIs written dead inside Spring, corresponding toBeanDefinitionThe implementation of the isRootBeanDefinition
  • The injected configuration class uses the specified name, if any, or the class name, if none. The corresponding code is as follows:
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
   if (definition instanceof AnnotatedBeanDefinition) {
       // Determine the name of the bean by scribing
       String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
       if (StringUtils.hasText(beanName)) {
 // Explicit bean name found.  return beanName;  }  }  // Generate a default unique beanName  return buildDefaultBeanName(definition, registry); } Copy the code

DetermineBeanNameFromAnnotation () is through comments to generate a beanName. For example, the class whose name is specified by the attribute value in the annotation ends up in the container as beanName.

protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
    // Get the annotation on the class
    AnnotationMetadata amd = annotatedDef.getMetadata();
    // Get the type of the annotation
    Set<String> types = amd.getAnnotationTypes();
 String beanName = null;  for (String type : types) {  AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);  if(attributes ! =null && isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {  // Get the value attribute  Object value = attributes.get("value");  // beanName is specified if it is a string, otherwise null  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; } Copy the code

Here’s how to generate a default beanName:

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);
} Copy the code

In the figure above, there are two objects, beanNameGenerator and scopeMetadataResolver, that define scope parsers for the beanName generator and bean, respectively. In Spring these objects correspond to two BeanNameGenerator and ScopeMetadataResolver policy interfaces. The real processing logic is done in the implementation class, where the corresponding policy is obtained through the new implementation class. In AnnotatedBeanDefinitionReader corresponding code is as follows:

private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
Copy the code

This article is formatted using MDNICE