Spring IoC’s container architecture

IoC container is the core module of Spring, which abstracts object management and dependency management framework solution. Spring provides a number of containers, of which the BeanFactory is the top-level (root) container and cannot be instantiated. It defines a set of principles that all IoC containers must follow. Specific container implementations can add additional functionality, such as the common ApplicationContext. Its more specific implementation such as ClassPathXmlApplicationContext contains content, parse the XML and a series of AnnotationConfifigApplicationContext is includes annotations analytic and so on a series of content. The Spring IoC container inheritance system is smart enough to use whichever layer you need, rather than the full-featured one.

The BeanFactory top-level interface method stack is as follows

The BeanFactory container inheritance is as follows

From its interface design, we can see that the ApplicationContext we always use inherits the BeanFactory sub-interface, ResourceLoader, MessageSource, etc., so it provides richer functionality.

Below we ClasspathXmlApplicationContext, for example, deep source of IoC container initialization process.

BeanFactory creation process

Get the BeanFactory subprocess

Entry: org. Springframework. Context. Support. AbstractApplicationContext# refresh

/** * This method does the IOC container creation and initialization work * the most important steps in this method are the second and eleventh steps ** /
@Override
public void refresh(a) throws BeansException, IllegalStateException {
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      // STEP 1: refresh preprocessing
      // Preparations include setting the startup time, whether to activate the identity bit, and initializing the property source configuration
      prepareRefresh();

      // Tell the subclass to refresh the internal bean factory.
      / / use DefaultListableBeanFactory is DefaultListableBeanFactory subclass
      // Initialize the bean factory

      / / STEP 2:
      / / a) to create the IoC container (DefaultListableBeanFactory)
      // b) load the parsed XML file (eventually stored in the Document object)
      // c) read the Document object and complete the loading and registration of BeanDefinition
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Prepare the bean factory for use in this context.
      // The most important method, prepare the bean factory
      // STEP 3: Preprocess the IoC container (set some common attributes)
      prepareBeanFactory(beanFactory);

      try {
         // Allows post-processing of the bean factory in context subclasses.
         // This method does not have any code in the current version of Spring, perhaps spring expects it to be extended in a later version
         // Empty shell method
         postProcessBeanFactory(beanFactory);

         // Invoke factory processors registered as beans in the context.
         // More important method
         // Execute the registered Factory Processors in spring's environment
         / / set custom ProcessorFactory and defined within a spring (ConfigutationClassPoetProcessor)
         / / spring BeanFactoryPostProcessor ConfigurationClassPostProcessor is maintained within a spring
         / / the following method in the main executive ConfigurationClassPostProcessor method
         // STEP 5: Call BeanFactoryPostProcessor to process BeanDefinition

         /** * BeanFactoryPostProcessor is one of spring's extension points. Spring allows BeanFactoryPostProcessor to read its configured metadata before the container instantiates any other beans * and can modify it as needed, For example, you can change the scope of the bean from Singleton to Prototype. You can also change the value of the property to * You can configure multiple spring BeanFactoryPostProcessor at the same time, and by setting the 'order' properties to control each spring BeanFactoryPostProcessor * BeanFactoryPostProcessor is executed after the Bean definition file is loaded by the Spring container and before the bean is instantiated
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register bean processors that intercept bean creation.
         // The previous line of code already put some of the post-processors into bdMap, including the custom BeanPostProcessor
         // Register the BeanPostProcessor
         // Remove all the post-processors in bdMap,
         // Add some other post-processors to the factory list
         // STEP 6: Register the BeanPostProcessor
         registerBeanPostProcessors(beanFactory);

         // Initialize message source for this context.
         // Not important, internationalization processing
         // STEP 7: Initialize some message sources (such as i18N for internationalization)
         initMessageSource();

         // Initialize event multicaster for this context.
         // It is not important to handle the event
         // STEP 8: Initializes the application event broadcaster
         initApplicationEventMulticaster();

         // Initialize other special beans in specific context subclasses.
         // This is an empty shell method with no code in it
         // STEP 9: Initialize some special beans
         onRefresh();

         // Check for listener beans and register them.
         // Wait for some listener registrations
         STEP 10: Register some listeners
         registerListeners();

         // Instantiate all remaining (non-lazy-init) singletons.
         // key, key
         // Complete the bean instantiation
         // STEP 11: Instantiate the remaining singleton beans (not lazy-loaded)
         // Note: The IoC, DI, and AOP of beans all occur in this step
         finishBeanFactoryInitialization(beanFactory);

         // Last step: publish corresponding event.
         // STEP 12: Publish the corresponding events when the refresh is complete
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Propagate exception to caller.
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...resetCommonCaches(); }}}Copy the code

Then check the org. Springframework. Context. Support. AbstractRefreshableApplicationContext# refreshBeanFactory method.

/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the Context's lifecycle. * Actually call the method * destroy the previous container * create a new 'IoC container' * load the 'BeanDefinition' object to register with the IoC container */
@Override
protected final void refreshBeanFactory(a) throws BeansException {
   // If there was an IOC container before, destroy it
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      / / create the IOC container, namely DefaultListableBeanFactory
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      // Set factory properties: Whether BeanDefinition overrides are allowed and whether circular dependencies are allowed
      customizeBeanFactory(beanFactory);
      // Load the BeanDefinition object and register it in the IOC container (important)
      / / call real AbstractXmlApplicationContext loadBeanDefinitions method in class
      // Focus on this step
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory; }}catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for "+ getDisplayName(), ex); }}Copy the code

The BeanFactory is created in the second step: obtainFreshBeanFactory method

The sequence diagram for BeanFactory creation is as follows:

Be defidefifinition loading, resolution and registration subprocesses

The entry is in the same: obtainFreshBeanFactory method as above

/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the Context's lifecycle. * Actually call the method * destroy the previous container * create a new 'IoC container' * load the 'BeanDefinition' object to register with the IoC container */
@Override
protected final void refreshBeanFactory(a) throws BeansException {
   // If there was an IOC container before, destroy it
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      / / create the IOC container, namely DefaultListableBeanFactory
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      // Set factory properties: Whether BeanDefinition overrides are allowed and whether circular dependencies are allowed
      customizeBeanFactory(beanFactory);
      // Load the BeanDefinition object and register it in the IOC container (important)
      / / call real AbstractXmlApplicationContext loadBeanDefinitions method in class
      // Focus on this step
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory; }}catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for "+ getDisplayName(), ex); }}Copy the code

LoadBeanDefinitions (beanFactory

Let’s look at the way based on XML AbstractXmlApplicationContext class

/**
 * Loads the bean definitions via an XmlBeanDefinitionReader.
 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
 * @see #initBeanDefinitionReader
 * @see#loadBeanDefinitions * * Actually calls this method */
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   // Create a BeanDefinition reader to actually load and register BeanDefinition by reading the XML file
   // This is a delegate mode
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // Configure the bean definition reader with this context's
   // resource loading environment.
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // Allow a subclass to provide custom initialization of the reader,
   // then proceed with actually loading the bean definitions.
   initBeanDefinitionReader(beanDefinitionReader);
   / / the key
   // Delegate to BeanDefinition reader to load BeanDefinition
   loadBeanDefinitions(beanDefinitionReader);
}
Copy the code
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   // Obtain the location of the resource
   // getConfigResources is an empty implementation. The real implementation calls the subclass's method to get the resource location
   / / such as: during the ClassPathXmlApplicationContext implementation
   / / and FileSystemXmlApplicationContext without using this method
   Resource[] configResources = getConfigResources();
   if(configResources ! =null) {
      / / XML Bean reader calls its parent class AbstractBeanDefinitionReader loadBeanDefinitions reads in the positioning of the resources
      / / the key
      reader.loadBeanDefinitions(configResources);
   }
   / / if a subclass of resource location is empty, then obtain FileSystemXmlApplicationContext setConfigLocations method set up resources in the constructor
   String[] configLocations = getConfigLocations();
   if(configLocations ! =null) {
      / / XML Bean reader calls its parent class AbstractBeanDefinitionReader reads the positioning of the resources
      / / the keyreader.loadBeanDefinitions(configLocations); }}Copy the code

Then: org. Springframework. Beans. Factory. Support. AbstractBeanDefinitionReader# loadBeanDefinitions method

// Actually call the method
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
   Assert.notNull(locations, "Location array must not be null");
   int counter = 0;
   // If there are multiple config files, loop through the load and count how many beanDefinitions are loaded
   for (String location : locations) {
      counter += loadBeanDefinitions(location);
   }
   return counter;
}
Copy the code
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
   / / the key
   return loadBeanDefinitions(location, null);
}
Copy the code
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
   // Gets the resource loader set during IOC container initialization
   ResourceLoader resourceLoader = getResourceLoader();
   if (resourceLoader == null) {
      throw new BeanDefinitionStoreException(
            "Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
   }

   if (resourceLoader instanceof ResourcePatternResolver) {
      // Resource pattern matching available.
      try {
         Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
         // Delegate calls methods of its subclass XmlBeanDefinitionReader for loading
         int loadCount = loadBeanDefinitions(resources);
         if(actualResources ! =null) {
            for(Resource resource : resources) { actualResources.add(resource); }}if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
         }
         return loadCount;
      }
      catch (IOException ex) {
         throw new BeanDefinitionStoreException(
               "Could not resolve bean definition resource pattern [" + location + "]", ex); }}else {
      // Can only load single resources by absolute URL.
      Resource resource = resourceLoader.getResource(location);
      int loadCount = loadBeanDefinitions(resource);
      if(actualResources ! =null) {
         actualResources.add(resource);
      }
      if (logger.isDebugEnabled()) {
         logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
      }
      returnloadCount; }}Copy the code
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
   Assert.notNull(resources, "Resource array must not be null");
   int counter = 0;
   for (Resource resource : resources) {
      / / key points:
      // Actually call the loadBeanDefinitions method in subclass XmlBeanDefinitionReader
      counter += loadBeanDefinitions(resource);
   }
   return counter;
}
Copy the code

Then: org. Springframework. Beans. Factory. XML. XmlBeanDefinitionReader# loadBeanDefinitions method

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   // Perform special encoding on the read XML resources
   // Proceed to call the loadBeanDefinitions method below in this class
   return loadBeanDefinitions(new EncodedResource(resource));
}
Copy the code
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   Assert.notNull(encodedResource, "EncodedResource must not be null");
   if (logger.isInfoEnabled()) {
      logger.info("Loading XML bean definitions from " + encodedResource);
   }

   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
   if (currentResources == null) {
      currentResources = new HashSet<>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
   }
   if(! currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }
   try {
      // Convert the resource file to an I/O stream of InputStream
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
         // Get the parsing source of the XML from InputStream
         InputSource inputSource = new InputSource(inputStream);
         if(encodedResource.getEncoding() ! =null) {
            inputSource.setEncoding(encodedResource.getEncoding());
         }
         // Here is the specific reading process
         return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
      }
      finally{ inputStream.close(); }}catch (IOException ex) {
      throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
   }
   finally {
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
         this.resourcesCurrentlyBeingLoaded.remove(); }}}Copy the code
**
 * Actually load bean definitions from the specified XML file.
 * @param inputSource the SAX InputSource to read from
 * @param resource the resource descriptor for the XML file
 * @return the number of bean definitions found
 * @throws BeanDefinitionStoreException in case of loading or parsing errors
 * @see #doLoadDocument
 * @see#registerBeanDefinitions * This method is actually called * in this class, XmlBeanDefinitionReader */protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
      // Load and parse the XML file through dom4j to form the Document object
      Document doc = doLoadDocument(inputSource, resource);
      // Load and register BeanDefinition by operating on the Document object
      return registerBeanDefinitions(doc, resource);
   }
   catch (BeanDefinitionStoreException ex) {
      throw ex;
   }
   catch (SAXParseException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
            "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
   }
   catch (SAXException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
            "XML document from " + resource + " is invalid", ex);
   }
   catch (ParserConfigurationException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "Parser configuration exception parsing XML from " + resource, ex);
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "IOException parsing XML document from " + resource, ex);
   }
   catch (Throwable ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "Unexpected exception parsing XML document from "+ resource, ex); }}Copy the code

Org. Springframework. Beans. Factory. XML. XmlBeanDefinitionReader# doLoadBeanDefinitions method

The above is divided into two steps:

  • Use dom4J to parse the ApplicationContext.xml configuration file into a Document object
  • Will parse the Document object, and encapsulated into BeanDefinition object, and finally into the DefaultListableBeanFactory created above (beanDefinitionMap in understandable into the Ioc container).

The first step

Use dom4J to parse the ApplicationContext.xml configuration file into a Document object

/**
 * Actually load the specified document using the configured DocumentLoader.
 * @param inputSource the SAX InputSource to read from
 * @param resource the resource descriptor for the XML file
 * @return the DOM Document
 * @throws Exception when thrown from the DocumentLoader
 * @see #setDocumentLoader
 * @seeDocumentLoader#loadDocument * loads and parses the XML file through dom4j to form the Document object */
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
   return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
         getValidationModeForResource(resource), isNamespaceAware());
}
Copy the code

The specific analysis behind is not specific to see.

The second step

Will parse the Document object, and encapsulated into BeanDefinition object, and finally into the DefaultListableBeanFactory created above (beanDefinitionMap in understandable into the Ioc container).

Call the XmlBeanDefinitionReader registerBeanDefinitions method ** /
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   / / create BeanDefinitionDocumentReader used to parse the Document object
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   // Get the number of registered beans in the container
   int countBefore = getRegistry().getBeanDefinitionCount();
   / / the parsing process, entrance BeanDefinitionDocumentReader just an interface
   Completed in DefaultBeanDefinitionDocumentReader / / concrete implementation process
   / / the key
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   // Count the number of newly registered beans
   return getRegistry().getBeanDefinitionCount() - countBefore;
}
Copy the code

Then: org. Springframework. Beans. Factory. XML. DefaultBeanDefinitionDocumentReader# registerBeanDefinitions method


/** * to parse the Document object */
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
   this.readerContext = readerContext;
   logger.debug("Loading bean definitions");
   // Get the root node first
   // Get the 
      
        tag for the root element of the Document
      
   Element root = doc.getDocumentElement();
   / / the key
   doRegisterBeanDefinitions(root);
}
Copy the code
/**
 * Register each bean definition within the given root {@code< beans / >} element. * key * or call * 1 doRegisterBeanDefinitions method in the class. Using the delegate pattern, here will be specific to BeanDefinitionParserDelegate BeanDefinition analytical work to complete the * 2. Perform custom parsing before parsing Bean definitions to make the parsing process more extensible * 3. Entrusted to BeanDefinitionParserDelegate, starting from the root element of the Document to BeanDefinition parsing * * /
protected void doRegisterBeanDefinitions(Element root) {
   // Any nested <beans> elements will cause recursion in this method. In
   // order to propagate and preserve <beans> default-* attributes correctly,
   // keep track of the current (parent) delegate, which may be null. Create
   // the new (child) delegate with a reference to the parent for fallback purposes,
   // then ultimately reset this.delegate back to its original (parent) reference.
   // this behavior emulates a stack of delegates without actually necessitating one.

   / / use the delegate pattern, here will be specific to BeanDefinitionParserDelegate BeanDefinition analytical work to complete
   BeanDefinitionParserDelegate parent = this.delegate;
   this.delegate = createDelegate(getReaderContext(), root, parent);

   / / determine whether namespace http://www.springframework.org/schema/beans
   if (this.delegate.isDefaultNamespace(root)) {
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
               profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
         if(! getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (logger.isInfoEnabled()) {
               logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                     "] not matching: " + getReaderContext().getResource());
            }
            return; }}}// Perform custom parsing before parsing Bean definitions to make the parsing process more extensible
   preProcessXml(root);
   / / entrusted to BeanDefinitionParserDelegate, starting from the root element of the Document to BeanDefinition parsing
   / / the key
   parseBeanDefinitions(root, this.delegate);
   // After the Bean definition is parsed, custom parsing is performed to increase the extensibility of the parsing process
   postProcessXml(root);

   this.delegate = parent;
}
Copy the code
/**
 * Parse the elements at the root level in the document:
 * "import", "alias", "bean".
 * @paramThe DOM root element of the document is also called by the parseBeanDefinitions method, which is an AOP entry
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   // Does the loaded Documen object use Spring's default XML namespace (beans namespace)
   / / the beans in the tag XMLNS for http://www.springframework.org/schema/beans
   if (delegate.isDefaultNamespace(root)) {
      // Get all child nodes of the Document object root element (bean tags, Import tags, alias tags and other custom tags context, AOP, etc.)
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
         Node node = nl.item(i);
         if (node instanceof Element) {
            Element ele = (Element) node;
            // For bean tags, import tags, and alias tags, parsing rules are used by default
            // Note that any tag without a colon belongs to the default namespace, such as 
      
            if (delegate.isDefaultNamespace(ele)) {
               parseDefaultElement(ele, delegate);
            }
            // Elements like context tags, AOP tags,tx tags are parsed using user-defined parsing rules
            else{ delegate.parseCustomElement(ele); }}}}else {
      // If it is not the default namespace, the element nodes are resolved using user-defined parsing rulesdelegate.parseCustomElement(root); }}Copy the code
// Call the method in this class
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   // Parse 
      
        tags
      
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   // Parse the 
      
        tag
      
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   // Parse the 
      
        tag highlights
      
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   // Parse the built-in 
      
        tag
      
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      // recursive calldoRegisterBeanDefinitions(ele); }}Copy the code
/** * Process the given bean element, Parsing the bean definition * and registering it with the registry
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // Parse the 
      
        tag to get the BeanDefnition focus
      
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if(bdHolder ! =null) {
      // Decorate the BeanDefinition object if necessary
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // Register the final decorated instance.
         / / the key
         / / register ultimately BeanDefinition to BeanDefinitionRegistry (namely DefaultListableBeanFactory)
         / / because DefaultListableBeanFactory BeanDefinitionRegistry is realized
         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(newBeanComponentDefinition(bdHolder)); }}Copy the code

The above is divided into two steps:

  • First parse the Document into a BeanDefinitionHolder and put it in a BeanDefinitionHolder.
  • BeanDefinition object into beanDefinitionMap DefaultListableBeanFactory objects.

1. Parse the Document and encapsulate it into BeanDefinition and place it in BeanDefinitionHolder.

Then: org. Springframework. Beans. Factory. XML. BeanDefinitionParserDelegate# parseBeanDefinitionElement method

/**
 * Parses the supplied {@code <bean>} element. May return {@code null}
 * if there were errors during parse. Errors are reported to the
 * {@linkOrg. Springframework. Beans. Factory. Parsing. ProblemReporter}. * really calling the method: * /
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
   return parseBeanDefinitionElement(ele, null);
}
Copy the code
/**
 * Parses the supplied {@code <bean>} element. May return {@code null}
 * if there were errors during parse. Errors are reported to the
 * {@linkOrg. Springframework. Beans. Factory. Parsing. ProblemReporter}. * really calling the method: * /
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
   // Get the value of the attribute ID in the 
      
        tag
      
   String id = ele.getAttribute(ID_ATTRIBUTE);
   // Get the value of the attribute name in the 
      
        tag
      
   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.isDebugEnabled()) {
         logger.debug("No XML 'id' specified - using '" + beanName +
               "' as bean name and " + aliases + " as aliases"); }}if (containingBean == null) {
      // Check whether the id or name of the bean is unique
      checkNameUniqueness(beanName, aliases, ele);
   }

   // Parse the 
      
        tag to get the BeanDefinition object: important
      
   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.
               String beanClassName = beanDefinition.getBeanClassName();
               if(beanClassName ! =null &&
                     beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                     !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); }}if (logger.isDebugEnabled()) {
               logger.debug("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);
      // Encapsulate BeanDefinitionHolder in the BeanDefinitionHolder data structure
      return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
   }

   return null;
}
Copy the code
**
 * Parse the bean definition itself, without regard to name or aliases. May return
 * {@code null} ifProblems occurred during the parsing of the bean definition@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;
   // Get the parent attribute of the bean label
   if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
      parent = ele.getAttribute(PARENT_ATTRIBUTE);
   }

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

      // Set the attributes of the following parsed bean tags into the created BeanDefinition object, that is, encapsulate them all into the BeanDefinition object

      // Parse the attributes of the 
      
        tag
      
      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
      // Parse the child tags of the 
      
        tag
      
      // Parse the 
      
        tag
      
      bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

      // Parse 
       tags
      parseMetaElements(ele, bd);
      // parse the 
      
        tag
      
      parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
      // Resolve the < event-method > tag
      parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

      
       
      
      parseConstructorArgElements(ele, bd);
      // the 
      
        tag is used ----
      
      parsePropertyElements(ele, bd);
      Parse the 
      
        tag
      
      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;
}
Copy the code
/**
 * Create a bean definition for the given class name and parent name.
 * @param className the name of the bean class
 * @param parentName the name of the bean's parent bean
 * @return the newly created bean definition
 * @throwsClassNotFoundException if bean class resolution was runtime but failed
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
      throws ClassNotFoundException {

   return BeanDefinitionReaderUtils.createBeanDefinition(
         parentName, className, this.readerContext.getBeanClassLoader());
}
Copy the code

Then: org. Springframework. Beans. Factory. Support. BeanDefinitionReaderUtils# createBeanDefinition method

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

   // Creating the BeanDefinition object ---- finally comes to an end
   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;
}
Copy the code

2. BeanDefinition object into beanDefinitionMap DefaultListableBeanFactory objects.

Then: org. Springframework. Beans. Factory. Support. BeanDefinitionReaderUtils# registerBeanDefinition method

/**
 * Register the given bean definition with the given bean factory.
 * @param definitionHolder the bean definition including name and aliases
 * @param registry the bean factory to register with
 * @throwsBeanDefinitionStoreException if registration failed * really calling this method is very important, * * / put bd bdMap
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   // Register bean definition under primary name.
   String beanName = definitionHolder.getBeanName();
   // Split BeanDefinitionHolder key and value
   // Use this method to register bd into bdMap
   / / call the registerBeanDefinition DefaultListableBeanFactory complete real BeanDefinition registration
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // Register aliases for bean name, if any.
   String[] aliases = definitionHolder.getAliases();
   if(aliases ! =null) {
      for(String alias : aliases) { registry.registerAlias(beanName, alias); }}}Copy the code

Finally: org. Springframework. Beans. Factory. Support. DefaultListableBeanFactory# registerBeanDefinition method

// This method registers BeanDefinition with bdMap
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {

   Assert.hasText(beanName, "Bean name must not be empty");
   Assert.notNull(beanDefinition, "BeanDefinition must not be null");

   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
      catch (BeanDefinitionValidationException ex) {
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Validation of bean definition failed", ex);
      }
   }

   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   if(existingDefinition ! =null) {
      if(! isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
               "': There is already [" + existingDefinition + "] bound.");
      }
      else if (existingDefinition.getRole() < beanDefinition.getRole()) {
         // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
         if (logger.isWarnEnabled()) {
            logger.warn("Overriding user-defined bean definition for bean '" + beanName +
                  "' with a framework-generated bean definition: replacing [" +
                  existingDefinition + "] with [" + beanDefinition + "]"); }}else if(! beanDefinition.equals(existingDefinition)) {if (logger.isInfoEnabled()) {
            logger.info("Overriding bean definition for bean '" + beanName +
                  "' with a different definition: replacing [" + existingDefinition +
                  "] with [" + beanDefinition + "]"); }}else {
         if (logger.isDebugEnabled()) {
            logger.debug("Overriding bean definition for bean '" + beanName +
                  "' with an equivalent definition: replacing [" + existingDefinition +
                  "] with [" + beanDefinition + "]"); }}this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   else {
      if (hasBeanCreationStarted()) {
         // Cannot modify startup-time collection elements anymore (for stable iteration)
         synchronized (this.beanDefinitionMap) {
            this.beanDefinitionMap.put(beanName, beanDefinition);
            List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            updatedDefinitions.add(beanName);
            this.beanDefinitionNames = updatedDefinitions;
            if (this.manualSingletonNames.contains(beanName)) {
               Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
               updatedSingletons.remove(beanName);
               this.manualSingletonNames = updatedSingletons; }}}else {
         // Still in startup registration phase
         / / the key
         / / DefaultListableBeanFactory BeanDefinitionHolder of bd and beanName into the map in the collection
         this.beanDefinitionMap.put(beanName, beanDefinition);
         this.beanDefinitionNames.add(beanName);
         this.manualSingletonNames.remove(beanName);
      }
      this.frozenBeanDefinitionNames = null;
   }

   if(existingDefinition ! =null|| containsSingleton(beanName)) { resetBeanDefinition(beanName); }}Copy the code

conclusion

Parse the contents of the ApplicationContext.xml configuration file into a Document object using Dom4j, then parse the Document object and wrap it into a BeanDefinition object, Finally BeanDefinition object into DefaultListableBeanFactory object Map < String, BeanDefinition > beanDefinitionMap, the key to the bean’s id, BeanName, value is the corresponding BeanDifinition object, that is, the definition information of the class, such as beanName, singleton, lazy loading, attribute information, initialization method, etc.