IoC registration BeanDefinitions
After obtaining the XML Document object, the xmlbeandefinitionreader \registerbeandefinitions (document doc, Resource resource) method will be called according to the object and the Resource object to start the journey of registering BeanDefinitions. The code is as follows:
// AbstractBeanDefinitionReader.java private final BeanDefinitionRegistry registry; // XmlBeanDefinitionReader.java public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // <1> Create BeanDefinitionDocumentReader object BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // <2> Get the number of registered beandefinitions int countBefore = getRegistry().getBeanDefinitionCount(); // <3> Create XmlReaderContext object // <4> Register BeanDefinition documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // Calculate the number of newly registered beandefinitions return getRegistry().getBeanDefinitionCount() - countBefore; }
- <1> Call the \createBeanDefinitionDocumentReader() method to instantiate the BeanDefinitionDocumentReader object.
FROM "deep analysis of Spring source code", P16
Define read Document and register BeanDefinition function
- <2> Call the beandefinitionregistry\getbeandefinitioncount() method to get the number of registered beandefinitions.
- <3> Call the \createReaderContext(Resource resource) method to create an XmlReaderContext object.
- <4> At, call the BeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext) method to read XML elements and register beandefinitions.
- <5> Calculate the number of newly registered beandefinitions.
1. createBeanDefinitionDocumentReader
#createBeanDefinitionDocumentReader(), instantiate BeanDefinitionDocumentReader object. The code is as follows:
/** * documentReader Class of * * @see #createBeanDefinitionDocumentReader() */ private Class<? extends BeanDefinitionDocumentReader> documentReaderClass = DefaultBeanDefinitionDocumentReader.class; protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() { return BeanUtils.instantiateClass(this.documentReaderClass); }
- The default value of documentReaderClass is defaultbeandefinitiondocumentreader class . We will analyze it in detail in the following articles.
2. registerBeanDefinitions
BeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext) method, register BeanDefinition, and define it in the interface BeanDefinitionDocumentReader. The code is as follows:
public interface BeanDefinitionDocumentReader { /** * Read bean definitions from the given DOM document and * register them with the registry in the given reader context. * @param doc the DOM document * @param readerContext the current context of the reader * (includes the target registry and the resource being parsed) * @throws BeanDefinitionStoreException in case of parsing errors */ void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) throws BeanDefinitionStoreException; }
Parse the defined BeanDefinition from the given Document object and register them in the registry. Method receives two parameters:
- doc method parameter: the Document object to be parsed.
- readerContext method, the current context of the parser, including the target registry and the parsed resources. It is created according to Resource
2.1 DefaultBeanDefinitionDocumentReader
BeanDefinitionDocumentReader has and has only one default implementation class, DefaultBeanDefinitionDocumentReader. Its implementation code for the \registerBeanDefinitions(...) method is as follows:
DefaultBeanDefinitionDocumentReader provides an implementation of this method:
@Nullable private XmlReaderContext readerContext; @Nullable private BeanDefinitionParserDelegate delegate; /** * This implementation parses bean definitions according to the "spring-beans" XSD * (or DTD, historically). * <p>Opens a DOM Document; then initializes the default settings * specified at the {@code <beans/>} level; then parses the contained bean definitions. */ @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; // Get XML Document Root Element // Execute registration BeanDefinition doRegisterBeanDefinitions(doc.getDocumentElement()); } /** * Register each bean definition within the given root {@code <beans/>} element. */ @SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...) 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. // Record the old BeanDefinitionParserDelegate object BeanDefinitionParserDelegate parent = this.delegate; // <1> Create a BeanDefinitionParserDelegate object and set it to delegate this.delegate = createDelegate(getReaderContext(), root, parent); // <2> Check whether the namespace of the <beans / > root tag is empty, or http://www.springframework.org/schema/beans if (this.delegate.isDefaultNamespace(root)) { // <2.1> handle the profile attribute. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { // <2.2> using separator segmentation, there may be multiple profile s. String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // <2.3> if all profile s are invalid, do not register // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } // <3> Processing before parsing preProcessXml(root); // <4> Parsing parseBeanDefinitions(root, this.delegate); // <5> Post parsing processing postProcessXml(root); // Set delegate back to the old BeanDefinitionParserDelegate object this.delegate = parent; }
-
<1> Create a BeanDefinitionParserDelegate object and set it to delegate. BeanDefinitionParserDelegate is an important class, which is responsible for parsing BeanDefinition. The code is as follows:
FROM "deep analysis of Spring source code" P16
Define various methods for parsing XML elements
protected BeanDefinitionParserDelegate createDelegate( XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) { // Create BeanDefinitionParserDelegate object BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext); // Initialize default delegate.initDefaults(root, parentDelegate); return delegate; }
-
<2> Check whether the namespace of the <beans / > root tag is empty, or http://www.springframework.org/schema/beans .
- At <2.1>, judge whether the profile attribute is configured on <beans / >.
- At <2.2>, use separator segmentation, and there may be multiple profile s.
- At <2.3>, judge that if all profile s are invalid, return does not register.
-
<4> Call the \parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) method to parse the logic.
-
<3> / <5>, the processing before and after parsing. At present, these two methods are empty and implemented by subclasses. The code is as follows:
protected void preProcessXml(Element root) {} protected void postProcessXml(Element root) {}
2.1.1 parseBeanDefinitions
#parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) method to parse logic. The code is as follows:
/** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // <1> If the root node uses the default namespace, perform the default resolution if (delegate.isDefaultNamespace(root)) { // Traverse 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; // <1> If the node uses the default namespace, perform the default resolution if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); // If the node is not the default namespace, perform custom resolution } else { delegate.parseCustomElement(ele); } } } // <2> If the root node is not the default namespace, perform custom resolution } else { delegate.parseCustomElement(root); } }
-
Spring has
two types
Bean declaration method:
- Configuration file declaration: <bean id= "studentservice" class= "org.springframework.core.studentservice" / >. Corresponding to <1>.
- Custom annotation method: <tx: annotation driven >. Corresponding to <2>.
-
<1> If the root node or child node uses the default namespace, call the \parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) method to perform the default resolution. The code is as follows:
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // import importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // alias processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // bean processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // beans // recurse doRegisterBeanDefinitions(ele); } }
- For detailed analysis, see the following articles.
-
<2> If the root node or child node does not use the default namespace, call the BeanDefinitionParserDelegate#parseCustomElement(Element ele) method to perform custom resolution. For detailed analysis, see the following articles.
3. createReaderContext
#createReaderContext(Resource resource) method to create an XmlReaderContext object. The code is as follows:
private ProblemReporter problemReporter = new FailFastProblemReporter(); private ReaderEventListener eventListener = new EmptyReaderEventListener(); private SourceExtractor sourceExtractor = new NullSourceExtractor(); @Nullable private NamespaceHandlerResolver namespaceHandlerResolver; /** * Create the {@link XmlReaderContext} to pass over to the document reader. */ public XmlReaderContext createReaderContext(Resource resource) { return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, getNamespaceHandlerResolver()); }
For detailed parsing of XmlReaderContext, see subsequent articles.
4. Summary
So far, the three things done in the xmlbeandefinitionreader \doloadbeandefinitions (inputsource, inputsource, resource resource) method have been analyzed. The following will analyze and explain the parsing process of BeanDefinition in detail.
In addition, the overall sequence diagram of xmlbeandefinitionreader \doloadbeandefinitions (inputsource, inputsource, resource resource) method is as follows:
Sequence diagram
- The red box part is the parsing process of BeanDefinition.