Spring source reading - 4

Keywords: Spring xml Attribute

Creation of BeanDefinitionHolder

The inheritance structure of BeanDefinition in the Spring framework is as follows:

BeanDefinition is the internal representation of the configuration file < bean > element label in the container. The < bean > element tag has class, scope, lazy init and other configuration attributes, while BeanDefinition provides the corresponding beanClass, scope, lazyInit attributes. The attributes in BeanDefinition and < bean > correspond one by one.

When creating a BeanDefinition, you can generate instances of RootBeanDefinition or GenericBeanDefinition or ChildBeanDefinition. But now most of the time it's creating instances of GenericBeanDefinition.

  • RootBeanDefinition: the most commonly used implementation class, which corresponds to the general < bean > element label;
  • Genericbean definition: it is a newly added bean file configuration attribute definition class since 2.5, which is a one-stop service class;
  • ChildBeanDefinition: in the configuration file, the parent < bean > and the child < bean > can be defined. The parent < bean > is represented by RootBeanDefinition, while the child < bean > is represented by ChildBeanDefinition, while the < bean > without the parent < bean > is represented by RootBeanDefinition. AbstractBeanDefinition abstracts the common class information of the two.

Processing stage in the loading, parsing, processing and registration of BeanDefinition . You need to create a BeanDefinitionHolder object. The creation process is as follows:

  • processBeanDefinition in the DefaultBeanDefinitionDocumentReader class calls the method by which the agent creates the BeanDefinitionHolder object

  • protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
       BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
       if (bdHolder != null) {
          bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
          try {
             // Register the final decorated instance.
             BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
          }
          catch (BeanDefinitionStoreException ex) {
             getReaderContext().error("Failed to register bean definition with name '" +
                   bdHolder.getBeanName() + "'", ele, ex);
          }
          // Send registration event.
          getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
       }
    }

  • In the BeanDefinitionParserDelegate class

    • @Nullable
      public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
         return parseBeanDefinitionElement(ele, null);
      }

    • @Nullable
      public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
         String id = ele.getAttribute(ID_ATTRIBUTE);
         String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

         List<String> aliases = new ArrayList<>();
         if (StringUtils.hasLength(nameAttr)) {
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
         }

         String beanName = id;
         if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            beanName = aliases.remove(0);
            if (logger.isTraceEnabled()) {
               logger.trace("No XML 'id' specified - using '" + beanName +
                     "' as bean name and " + aliases + " as aliases");
            }
         }

         if (containingBean == null) {
            checkNameUniqueness(beanName, aliases, ele);
         }

         //Create BeanDefinition object here
         AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

         if (beanDefinition != null) {
            if (!StringUtils.hasText(beanName)) {
               try {
                  if (containingBean != null) {
                     beanName = BeanDefinitionReaderUtils.generateBeanName(
                           beanDefinition, this.readerContext.getRegistry(), true);
                  }
                  else {
                     beanName = this.readerContext.generateBeanName(beanDefinition);
                     // Register an alias for the plain bean class name, if still possible,
                     // if the generator returned the class name plus a suffix.
                     // This is expected for Spring 1.2/2.0 backwards compatibility.
                     String beanClassName = beanDefinition.getBeanClassName();
                     if (beanClassName != null &&
                           beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                           !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                        aliases.add(beanClassName);
                     }
                  }
                  if (logger.isTraceEnabled()) {
                     logger.trace("Neither XML 'id' nor 'name' specified - " +
                           "using generated bean name [" + beanName + "]");
                  }
               }
               catch (Exception ex) {
                  error(ex.getMessage(), ele);
                  return null;
               }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
         }

         return null;
       

    • @Nullable
      public AbstractBeanDefinition parseBeanDefinitionElement(
            Element ele, String beanName, @Nullable BeanDefinition containingBean) {

         this.parseState.push(new BeanEntry(beanName));

         String className = null;
         if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
            className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
         }
         String parent = null;
         if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
         }

         try {
           AbstractBeanDefinition bd = createBeanDefinition(className, parent);

            parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
            bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

            parseMetaElements(ele, bd);
            parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
            parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

            parseConstructorArgElements(ele, bd);
            parsePropertyElements(ele, bd);
            parseQualifierElements(ele, bd);

            bd.setResource(this.readerContext.getResource());
            bd.setSource(extractSource(ele));

            return bd;
         }
         catch (ClassNotFoundException ex) {
            error("Bean class [" + className + "] not found", ele, ex);
         }
         catch (NoClassDefFoundError err) {
            error("Class that bean class [" + className + "] depends on not found", ele, err);
         }
         catch (Throwable ex) {
            error("Unexpected failure during bean definition parsing", ele, ex);
         }
         finally {
            this.parseState.pop();
         }
         return null;
      }

    • protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
            throws ClassNotFoundException {

         return BeanDefinitionReaderUtils.createBeanDefinition(
               parentName, className, this.readerContext.getBeanClassLoader());

      }

  • In the BeanDefinitionReaderUtils class

    • BeanDefinition instance created
    • public static AbstractBeanDefinition createBeanDefinition(
            @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader)
            throws ClassNotFoundException {

         GenericBeanDefinition bd = new GenericBeanDefinition();
         bd.setParentName(parentName);
         if (className != null) {
            if (classLoader != null) {
               bd.setBeanClass(ClassUtils.forName(className, classLoader));
            }
            else {
               bd.setBeanClassName(className);
            }
         }
         return bd;
      }

  • That's how to create a bean definition during the processing phase.

Posted by dakkonz on Tue, 31 Dec 2019 18:11:42 -0800