Spring source code learning series 2.2 - from document to beanDefinition

This chapter discusses the process from document to beanDefinition- registered beanDefinition to container beanDefinitionMap

from xml to document

from document to beanDefinition

from beanDefinton to instance


. Some of the main classes or interfaces involved:
org.springframework.beans.factory.xml.BeanDefinitionDocumentReader

org.springframework .beans.factory.xml.DefaultBeanDefinitionDocumentReader

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate

org.springframework.beans.factory.support.BeanDefinitionReaderUtils

org.springframework.beans.factory.config.BeanDefinition

org.springframework.beans.factory.xml .DefaultNamespaceHandlerResolver resolves


from document to beanDefinition <

bean> Starting with the most direct xml configuration <bean>


For tags such as transaction tx:annotation, there are specific handler classes configured under spring-tx.jar/META-INF/spring.handlers. Does the bean have the same configuration? In fact, we find the entry for parsing the registration tag.

Omit the process of loading from web.xml to doc, start from parsing Element root = doc.getDocumentElement() The task of

parsing

root node root is started by DefaultBeanDefinitionDocumentReader Source of DefaultBeanDefinitionDocumentReader:
http://www.grepcode.com/file/repository.springsource. com/org.springframework/org.springframework.beans/3.2.2/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java/


DefaultBeanDefinitionDocumentReader.parseBeanDefinitions
/**
	 * 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) {
		if (delegate.isDefaultNamespace(root)) {
			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;
					if (delegate.isDefaultNamespace(ele)) {
//1. Process the default label
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

For default tags like "import", "alias", "bean" parsing starts at parseDefaultElement. Non-default tags need to define schema/handlers for processing.


View the processing method of the default label:
DefaultBeanDefinitionDocumentReader#parseDefaultElement
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//1.1 Processing import tags
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(it);
		}
//1.2 Handling alias tags
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
//1.3 Handling bean tags
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(he);
		}
	}


Continue bean tag processing BEAN_ELEMENT:
DefaultBeanDefinitionDocumentReader#processBeanDefinition
/**
	 * Process the given bean element, parsing the bean definition
	 * and registering it with the registry.
	 */
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//1.3.1 Generated beanDefinitionHolder
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement (ele);
		if (bdHolder != null) {
//1.3.2 Parse custom elements of bean tags
//Extend BeanDefinition-custom label
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
//1.3.3 Register beanDefinitionMap
				// 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));
		}
	}


As can be seen from the above code, DefaultBeanDefinitionDocumentReader delegates the task of parsing and generating beanDefinition to BeanDefinitionParserDelegate for processing.


beanDefinitionDocumentReader
-------------------------------------------------- ------The dividing line that connects the previous and the next --------------------------------------- ------
beanDefinitionParserDelegate



BeanDefinitionParserDelegate source code:
http://grepcode.com/file/repository.springsource.com/org.springframework/org.springframework.beans/3.2.2/org/springframework/beans/factory/xml/ BeanDefinitionParserDelegate.java#BeanDefinitionParserDelegate

As can be seen from the above, the processing of bean tags is divided into three parts:
one is to parse the bean tags to generate BeanDefinitionHolder- processing spring default elements or attributes
BeanDefinitionParserDelegate#parseBeanDefinitionElement
/**
	 * Parses the supplied {@code <bean>} element. May return {@code null}
	 * if there were errors during parse. Errors are reported to the
	 * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
	 */
	public BeanDefinitionHolder pressBeanDefinitionElement (Element ele, BeanDefinition containingBean) {
		String id = ele.getAttribute(ID_ATTRIBUTE);
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		List<String> aliases = new ArrayList<String>();
		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) {
			checkNameUniqueness (beanName, aliases, ele);
		}

		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.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);
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}

		return null;
	}

It can be seen that the beanDefinition is first generated and then packaged into a beanDefinitionHolder
(1) The process of generating a beanDefinition
BeanDefinitionParserDelegate#createBeanDefinition
protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
			throws ClassNotFoundException {

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


创建GenericBeanDefinition

BeanDefinitionReaderUtils#createBeanDefinition
public static AbstractBeanDefinition createBeanDefinition(
			String parentName, String className, 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;
	}


You can use your imagination here. Just like the process of defining a model and then setting various properties. The attributes or subtags processed here are all attributes or tags defined by spring by default. The custom tags will be processed in the second step.

bd.setBeanClass involves a process from benDefintion to instance: resolveBeanClass
If beanClass is set here, resolveBeanClass can return directly.

Will beanClass be set here?
The key point is this.readerContext.getBeanClassLoader(), which continues to call XmlBeanDefinitionReader.getBeanClassLoader();
see <spring source code learning series 2.1-from xml to document> When creating an XmlBeanDefinitionReader object, the value of beanClassLoader is not set. The value of the beanDefinition defined


here The actual class is GenericBeanDefinition. In the process from beanDefinition to instance, it will be converted to RootBeanDefinition.

Usually we only set a few familiar properties. In fact, there are many properties in beanDefinition,
such as: lookup-method replace-method

How does spring parse these What about special attributes or tags to generate instances?

For other properties, see the source code of BeanDefinitionParserDelegate, which basically defines all properties with static variables

(2) The beanDefinitionHolder object generated at this time is also very simple, just storing the beanDefinition and its name, etc.


Second , decorating the BeanDefinitionHolder object generated in the first step - processing custom tags or attributes - can be regarded as an extension of spring - for spring the default label to expand

public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
			Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {

		BeanDefinitionHolder finalDefinition = definitionHolder;

		// Decorate based on custom attributes first.
		NamedNodeMap attributes = ele.getAttributes();
		for (int i = 0; i < attributes.getLength(); i++) {
			Node node = attributes.item(i);
			finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
		}

		// Decorate based on custom nested elements.
		NodeList children = ele.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			Node node = children.item(i);
			if (node.getNodeType() == Node.ELEMENT_NODE) {
				finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
			}
		}
		return finalDefinition;
	}

// Decorate based on custom attributes first.
NamedNodeMap attributes = ele.getAttributes();

// Decorate based on custom nested elements.
NodeList children = ele.getChildNodes();

Here we continue to cycle through the attributes or sub-labels of the bean tag, is it to continue In-depth processing of the attributes of the first step? Actually not, continue to follow the decorateIfRequired method to see
private BeanDefinitionHolder decorateIfRequired(
			Node node, BeanDefinitionHolder originalDef, BeanDefinition containingBd) {

		String namespaceUri = getNamespaceURI(node);
		if (!isDefaultNamespace(namespaceUri)) {
			NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
			if (handler != null) {
				return handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
			}
			else if (namespaceUri != null && namespaceUri.startsWith("http://www.springframework.org/")) {
				error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
			}
			else {
				// A custom namespace, not to be handled by Spring - maybe "xml:...".
				if (logger.isDebugEnabled()) {
					logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
				}
			}
		}
		return originalDef;
	}

In this code, there is a very important molecular judgment! isDefaultNamespace(namespaceUri), that is, whether it is a custom tag, if not, continue to obtain the corresponding namespace processing class for parsing.

Visible decoration is mainly the parsing of custom subtags or attributes process - extension to spring bean tags

NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);


This.readerContext.getNamespaceHandlerResolver() gets the DefaultNamespaceHandlerResolver, whose main responsibility is to manage namespace processors. The data source is mainly the content under META-INF/spring.handlers. Loading these files uses the lazy loading mechanism (Load the specified NamespaceHandler mappings lazily). In the

resolve method, first load the META-INF/spring.handler file:
Properties mappings =
								PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);


And convert the files in its Properties class to hashMap and cache them:
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);


Then, according to the namespace, read the corresponding namespace processor, and initialize the return:
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);


Put namespaceHandler into handlerMappings (previously convert Properties to hashMap, value value is only string type)
handlerMappings.put(namespaceUri, namespaceHandler);





The source code can be traced, referring to the DefaultNamespaceHandlerResolver source code of how spring reads the configuration file :
http://grepcode.com/file/repository.springsource.com/org.springframework/org.springframework.beans/3.2.2/org/springframework /beans/factory/xml/DefaultNamespaceHandlerResolver.java/


The third is to register with the beanDefinition container
// Register the final decorated instance.
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());


/**
	 * 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
	 * @throws BeanDefinitionStoreException if registration failed
	 */
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

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

Here registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); is actually placed in DefaultListableBeanFactory.beanDefinitionMap


So far , the analysis from document to beanDefinition ends

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326494471&siteId=291194637