Spring IOC – BeanDefinition parsing

1. Properties of BeanDefinition

BeanDefinition as an interface defines the get and set methods of properties. These attributes are basically defined in its direct implementation class AbstractBeanDefinition. The meaning of each attribute is as shown in the following table:

Type

Name

Meaning

Constant

SCOPE_DEFAULT

Default scope: singleton mode

AUTOWIRE_NO

no autowiring

AUTOWIRE_BY_NAME

Autowire by name

AUTOWIRE_BY_TYPE

Autowire by type

AUTOWIRE_CONSTRUCTOR

Automatically assemble according to the most matching construction method

AUTOWIRE_AUTODETECT

Introspect bean classes to determine appropriate autowiring strategies

DEPENDENCY_CHECK_NONE

No dependency dependency checking

DEPENDENCY_CHECK_OBJECTS

Dependency checking on object references

DEPENDENCY_CHECK_SIMPLE

Dependency checking for simple properties

DEPENDENCY_CHECK_ALL

Dependency checks on all properties

INFER_METHOD

The container should attempt to infer the destruction method name instead of explicitly specifying the method name

Attribute variables

beanClass

Bean’s Class object

scope

Scope, default is singleton

abstractFlag

Whether it is an abstract class tag. The default value is false. If it is true, the bean will not be instantiated.

lazyInit

Whether to lazy load

autowireMode

Automatic assembly mode, none by default

dependencyCheck

Dependency check, default is none

dependsOn

The names of other beans on which the bean depends

autowireCandidate

Whether this bean is a candidate for autowiring to other beans, defaults to true

priary

Whether this bean is the primary autowiring candidate, defaults to false

Qulifiers

Automatic assembly candidate qualifier map, key: class object name (full class name)

instanceSupplier

Specify a callback function for creating bean instances as an alternative to declarative factory methods

nonPublicAccessAllowed

Whether to allow access to non-public constructors and methods, the default is true, and the flag is allowed

lenientConstructorResolution

Whether to parse the constructor in relaxed mode or strict mode. The default is true, which is relaxed mode.

factoryBeanName

Factory bean name

factoryMethodName

Factory method name

constructorArgumentValues

Constructor parameter values

propertyValues

attribute value

methodOverrides

The method that the current bean is overriding

initMethodName

The name of the initialization method

destroyMethodName

The name of the method to be destroyed

enforceInitMethod

Specifies whether the configured method is an initialization method, the default is true

enforceDestroyMethod

Specifies whether the configured method is a destruction method, the default is true

synthetic

Whether the beanDefiniton is synthetic, the default is false

role

Role hint means that the BeanDefinition is the main part of the application. Usually a user-defined bean

description

beanDefiniton human-readable description

resource

The resource the beanDefinition comes from

2. Three subcategories and attributes

AbstractBeanDefinition has three important direct subclasses. In addition to the attributes of the parent class, they also have their own special attributes. First, their class diagram is as follows:

The properties of each of the three direct subclasses are shown in the following table:

Subcategory

Attribute name

Attribute meaning

GenericBeanDefinition

parentName

parent bean name

ChildBeanDefinition

parentName

parent bean name

RootBeanDefinition

decoratedDefinition

Recorded beanName, alias, etc.

qualifiedElement

Documented annotation elements

stale

Whether the beanDefinition needs to be re-merged

allowCaching

Whether to allow caching, default is true

isFactoryMethodUnique

Whether the factory method is unique, the default is false

resolvedTargetType

Cache class, indicating which class information RootBeanDefinition stores

isFactoryBean

Table name whether the bean is a factory bean

factoryMethodReturnType

Return type of cache factory method

resolvedDestroyMethodName

Cache resolved destruction method names

resolvedConstructorOrFactoryMethod

Cache resolved constructor or factory method

constructorArgumentsResolved

Indicates whether the constructor parameters have been parsed. The default is false.

resolvedConstructorArguments

Cache resolved constructor parameters

preparedConstructorArguments

Cache constructor parameters to be parsed

postProcessed

Whether the table name has been processed by MergedBeanDefinitionPostProcessor, the default is false

beforeInstantiationResolved

Used when generating a proxy, whether the table name has already generated a proxy

externallyManagedConfigMembers

Members of the Contractor, Field, and method types are recorded

externallyManagedInitMethods

The init callback function name in InitializingBean for life cycle callback

externallyManagedDestroyMethods

The destroy callback function name of DisposableBean for life cycle callback

3. Applications and differences of the three subcategories

When defining a bean in an XML file, Spring will create a RootBeanDefinition instance, which will save all configuration information, such as class name, attribute values, etc.;

When a bean inherits another bean, Spring will create a ChildBeanDefinition for the child bean and a RootBeanDefinition for the parent bean;

When defining a bean using the @Bean annotation in the configuration class, Spring will create a GenericBeanDefinition instance;

When using annotations (such as @Component, @Service, @Repository, etc.) on a class to define a bean, Spring will create an instance that implements the AnnotatedBeanDefinition interface: AnnotatedGenericBeanDefinition or ScannedGenericBeanDefinition. This instance will save the class name, class type, and all annotation information on the class;

The main differences between GenericBeanDefinition and AnnotatedBeanDefinition are as follows:

AnnotatedBeanDefinition saves the annotation information on the class, but GenericBeanDefinition does not. This enables Spring to read and process these annotations at runtime, providing richer functionality.

4. Merger of BeanDefinition

Spring provides a variety of BeanDefinitions, which will eventually be converted or merged into RootBeanDefinition, that is, the configuration information of the child BeanDefinition and the parent BeanDefinition Configuration information is combined to form a complete configuration information. The merged BeanDefinition object contains all the information required for Bean creation. Spring will use this complete BeanDefinition to create Bean instances.

5. Merge analysis of BeanDefinition

The code is located at: AbstractBeanFactory#getMergedBeanDefinition

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;
         //The parent bean is empty
         if (bd.getParentName() == null) {
            // Use copy of given root bean definition.
            if (bd instanceof RootBeanDefinition) {
               // The original BeanDefinition is RootBeanDefinition, cloned directly
               mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
            }
            else {
               //The original BeanDefinition is not RootBeanDefinition, then a new one
               mbd = new RootBeanDefinition(bd);
            }
         }
         else {
            //The parent bean is not empty and needs to be merged
            // Child bean definition: needs to be merged with parent.
            BeanDefinition pbd;
            try {
               String parentBeanName = transformedBeanName(bd.getParentName());
               if (!beanName.equals(parentBeanName)) {
                  // Get the RootBeanDefinition corresponding to the parentBeanName in the map
                  pbd = getMergedBeanDefinition(parentBeanName);
               }
               else {
                  BeanFactory parent = getParentBeanFactory();
                  if (parent instanceof ConfigurableBeanFactory) {
                     pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
                  }
                  else {
                     throw new NoSuchBeanDefinitionException(parentBeanName,
                           "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
                           "': cannot be resolved without a ConfigurableBeanFactory parent");
                  }
               }
            }
            catch (NoSuchBeanDefinitionException ex) {
               throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
                     "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
            }
            // Deep copy with overridden values.
            // deep copy
            mbd = new RootBeanDefinition(pbd);
            // The information related to the BeanDefinition initialized by the child bean overwrites the same information inherited from the RootBeanDefinition.
            mbd.overrideFrom(bd);
         }

         // Set default singleton scope, if not configured before.
         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.
         if (containingBd != null & amp; & amp; !containingBd.isSingleton() & amp; & amp; 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 & amp; & amp; isCacheBeanMetadata()) {
            this.mergedBeanDefinitions.put(beanName, mbd);
         }
      }
      if (previous != null) {
         copyRelevantMergedBeanDefinitionCaches(previous, mbd);
      }
      returnmbd;
   }
}

The logic flow diagram is as follows:

Summary as follows:

If the bean has no father, generate a new RootBeanDefinition by cloning or deep copying the current beanDefinition and return it;

If the bean has a father, clone or deep copy the parent beanDefinition to generate a new RootBeanDefinition, then overwrite the child beanDefinition property with the same information of the RootBeanDefiniton, and return the RootBeanDefinition.