“This is the 29th day of my participation in the November Gwen Challenge. See details of the event: The Last Gwen Challenge 2021”.

IOC container initialization process

ClassPathResource resource = new ClassPathResource("bean.xml"); // <1> DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // <2> XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); // <3> reader.loadBeanDefinitions(resource); / / < 4 >Copy the code

The initialization process consists of three steps: resource location, resource loading, and resource registration

  • Resource localization: Because Bean objects are typically described through external resources, the first step in initializing the IOC container is to locate this external resource
  • Loading: Loading is BeanDefinition loading. Resource resources are read and parsed using BeanDefinitionReader to represent user-defined beans as IOC’s internal data structure: BeanDefinition.
  • Register: Register the resolved BeanDefinition with the IOC container in step 2. The IOC container maintains a HashMap, into which registration is used to inject BeanDefinition.

LoadBeanDefinitions (Container Initialization core method)

Parsing and registration are done through the loadBeanDefinitions method of XmlBeanDefinitionReader

// XmlBeanDefinitionReader.java@Overridepublic int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { return loadBeanDefinitions(new EncodedResource(resource)); }Copy the code

Pass in the defined Resource and encapsulate it to code the Resource. Then look at the internal loadBeanDefinitions(New EncodedResource(Resource));

Public int loadBeanDefinitions (EncodedResource EncodedResource) throws BeanDefinitionStoreException {/ / obtain the resources have been loaded Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get(); // Add the current resource to the record and throw an exception if (! ((Set)currentResources).add(encodedResource)) { throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!" ); } else { int var5; Try {// Get the encapsulated Resource from EncodedResource, and then get InputStream InputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() ! = null) { inputSource.setEncoding(encodedResource.getEncoding()); } / / core logic, the implementation of load and registered BeanDefinition var5 = this. DoLoadBeanDefinitions (inputSource, encodedResource getResource ()); } finally { inputStream.close(); } } catch (IOException var15) { throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15); } finally {// Remove resources from cache ((Set)currentResources).remove(encodedResource); if (((Set)currentResources).isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } return var5; }}Copy the code
  • If encodeedResource already exists, it will throw an exception. This method is used to prevent EncodedResource from loading itself before it is completed, which will lead to an infinite loop
  • Get Resource. Passing parameters from the Resource stream to doLoadBeanDefinitions(InputSource InputSource, Resource Resource) is the real logic for loading and registering the BeanDefinition.

doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, The Resource the Resource) throws BeanDefinitionStoreException {/ / retrieve XML Document instance Document doc = this.doLoadDocument(inputSource, resource); / / according to the Document instance, registered Bean information return enclosing registerBeanDefinitions (doc, resource); }Copy the code
  • The first thing is to parse the XML file and get the Document instance
  • How to register the Bean information according to the Document instance obtained in the statement

Register Bean information

// XmlBeanDefinitionReader.java public int registerBeanDefinitions(Document doc, The Resource the Resource) throws BeanDefinitionStoreException {/ / create BeanDefinitionDocumentReader object BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader(); Int countBefore = this.getregistry ().getBeandefinitionCount (); // Create the xmlReaderContext object, Registered BeanDefinition documentReader. RegisterBeanDefinitions (doc, enclosing createReaderContext (resource)); Return this.getregistry ().getBeandefinitionCount () -countbefore; return this.getregistry ().getBeandefinitionCount () -countbefore; }Copy the code
  • CreateBeanDefinitionDocumentReader () create BeanDefinitionDocumentReader object, the object is used to define read the Document and register BeanDefinition function.
  • RegisterBeanDefinitions is used to read XML elements and registerBeanDefinitions

BeanDefinitionDocumentReader# registerBeanDefinitions (Document, doc, XmlReaderContext readerContext) method, Registered BeanDefinition, defined in the interface BeanDefinitionDocumentReader

public interface BeanDefinitionDocumentReader {
    void registerBeanDefinitions(Document var1, XmlReaderContext var2) throws BeanDefinitionStoreException;
}
Copy the code
DefaultBeanDefinitionDocumentReader

Specific registerBeanDefinitions method implementation is done through this class, this is the default implementation BeanDefinitionDocumentReader class

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; this.logger.debug("Loading bean definitions"); // Get the XML DocumentElement Element root = doc.getDocumentElement(); / / execution registered BeanDefinition enclosing doRegisterBeanDefinitions (root); }Copy the code
doRegisterBeanDefinitions
Protected void doRegisterBeanDefinitions Element (root) {/ / record old BeanDefinitionParserDelegate object BeanDefinitionParserDelegate parent = this.delegate; / / create BeanDefinitionParserDelegate object, Delegate this.delegate = this.createdelegate (this.getreaderContext (), root, parent); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute("profile"); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; "); if (! this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (this.logger.isInfoEnabled()) { this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource()); } return; }} // Handle this.preprocessxml (root) before parsing; / / parsing enclosing parseBeanDefinitions (root, enclosing the delegate); // This. PostProcessXml (root); this.delegate = parent; }Copy the code
  • Create BeanDefinitionParserDelegate object, the object is used to resolve BeanDefinition
  • Both the pre-parsing and post-parsing methods are empty implementations and are left to subclasses
  • parseBeanDefinitions
Protected void parseBeanDefinitions (Element root, BeanDefinitionParserDelegate delegate) {/ / root node using the default namespace, Perform a default parsing the if (delegate isDefaultNamespace (root)) {/ / traverse the child nodes 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; / / root node using the default namespace, perform a default parsing the if (delegate) isDefaultNamespace (ele)) {enclosing parseDefaultElement (ele, delegate); } else {/ / root node using the default namespace, perform custom analytic delegate. ParseCustomElement (ele); } } } } else { delegate.parseCustomElement(root); }}Copy the code

There are two ways to declare beans in Spring

  • Configuration file declaration:. Corresponds to the default namespace
  • Custom annotation mode: TX :annotation-driven. Corresponding to a non-default namespace
ParseDefaultElement method
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, "import")) { this.importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, "alias")) { this.processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, "bean")) { this.processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, "beans")) { this.doRegisterBeanDefinitions(ele); }}Copy the code

The focus here is on the processBeanDefinition method, which does the core work of Bean label parsing

// DefaultBeanDefinitionDocumentReader.java protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {/ / Bean element analytical / / BeanDefinitionHolder BeanDefinition objects for the name and alias BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder ! = null) {/ / custom tag processing bdHolder = delegate. DecorateBeanDefinitionIfRequired (ele, bdHolder); Try {/ / registered BeanDefinition BeanDefinitionReaderUtils. RegisterBeanDefinition (bdHolder, this.getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException var5) { this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5); } // Issue a response event to notify the relevant listener, This.getreadercontext ().FireComponentRegistered (New BeanComponentDefinition(bdHolder)); }}Copy the code

There are three steps to parsing

  1. Parsing default tags
  2. Resolves custom tags under default tags
  3. Register the parsed BeanDefinition

Register BeanDefinition

Registered BeanDefinition is by BeanDefinitionReaderUtils registerBeanDefinition () finish.

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {/ / registered beanName String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); / / register alias String [] aliases. = definitionHolder getAliases (); if (aliases ! = null) { String[] var4 = aliases; int var5 = aliases.length; for(int var6 = 0; var6 < var5; ++var6) { String alias = var4[var6]; registry.registerAlias(beanName, alias); }}}Copy the code
  • First, register a BeanDefinition through beanName
  • The alias and beanName mappings are then registered.

BeanDefinitionRegistry

Registration of BeanDefinitions, defined by the interface BeanDefinitionRegistry

Register through beanName
// DefaultListableBeanFactory.java public void registerBeanDefinition(String beanName, BeanDefinition BeanDefinition) throws BeanDefinitionStoreException {/ / calibration beanName BeanDefinition is not empty 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 var9) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var9); }} // Get the BeanDefinition BeanDefinition existingDefinition = of the specified beanName from the cache (BeanDefinition)this.beanDefinitionMap.get(beanName); / / allow coverage, directly to cover the original BeanDefinition into beanDefinitionMap enclosing beanDefinitionMap. Put (BeanDefinition beanName); / / a BeanDefinition is added to the beanDefinitionMap enclosing beanDefinitionMap. Put (BeanDefinition beanName); / / add beanName into beanDefinitionNames List < String > updatedDefinitions = new ArrayList (this) beanDefinitionNames) size () + 1);Copy the code

The core of this code is beandefinitionMap. put(beanName, beanDefinition); Save the BeanDefinition to the Map.

conclusion